diff options
Diffstat (limited to 'src')
475 files changed, 10704 insertions, 5533 deletions
diff --git a/src/analyze/analyze-verify.c b/src/analyze/analyze-verify.c index a377996a37..deb102c22c 100644 --- a/src/analyze/analyze-verify.c +++ b/src/analyze/analyze-verify.c @@ -23,6 +23,7 @@ #include "alloc-util.h" #include "analyze-verify.h" +#include "bus-error.h" #include "bus-util.h" #include "log.h" #include "manager.h" @@ -164,7 +165,6 @@ static int verify_documentation(Unit *u, bool check_man) { static int verify_unit(Unit *u, bool check_man) { _cleanup_bus_error_free_ sd_bus_error err = SD_BUS_ERROR_NULL; - Job *j; int r, k; assert(u); @@ -173,11 +173,9 @@ static int verify_unit(Unit *u, bool check_man) { unit_dump(u, stdout, "\t"); log_unit_debug(u, "Creating %s/start job", u->id); - r = manager_add_job(u->manager, JOB_START, u, JOB_REPLACE, false, &err, &j); - if (sd_bus_error_is_set(&err)) - log_unit_error(u, "Error: %s: %s", err.name, err.message); + r = manager_add_job(u->manager, JOB_START, u, JOB_REPLACE, &err, NULL); if (r < 0) - log_unit_error_errno(u, r, "Failed to create %s/start: %m", u->id); + log_unit_error_errno(u, r, "Failed to create %s/start: %s", u->id, bus_error_message(&err, r)); k = verify_socket(u); if (k < 0 && r == 0) diff --git a/src/analyze/analyze.c b/src/analyze/analyze.c index 27ead903e9..e922d6fb32 100644 --- a/src/analyze/analyze.c +++ b/src/analyze/analyze.c @@ -1066,20 +1066,17 @@ static int graph_one(sd_bus *bus, const UnitInfo *u, char *patterns[], char *fro assert(bus); assert(u); - if (arg_dot == DEP_ORDER ||arg_dot == DEP_ALL) { + if (IN_SET(arg_dot, DEP_ORDER, DEP_ALL)) { r = graph_one_property(bus, u, "After", "green", patterns, from_patterns, to_patterns); if (r < 0) return r; } - if (arg_dot == DEP_REQUIRE ||arg_dot == DEP_ALL) { + if (IN_SET(arg_dot, DEP_REQUIRE, DEP_ALL)) { r = graph_one_property(bus, u, "Requires", "black", patterns, from_patterns, to_patterns); if (r < 0) return r; - r = graph_one_property(bus, u, "RequiresOverridable", "black", patterns, from_patterns, to_patterns); - if (r < 0) - return r; - r = graph_one_property(bus, u, "RequisiteOverridable", "darkblue", patterns, from_patterns, to_patterns); + r = graph_one_property(bus, u, "Requisite", "darkblue", patterns, from_patterns, to_patterns); if (r < 0) return r; r = graph_one_property(bus, u, "Wants", "grey66", patterns, from_patterns, to_patterns); @@ -1088,9 +1085,6 @@ static int graph_one(sd_bus *bus, const UnitInfo *u, char *patterns[], char *fro r = graph_one_property(bus, u, "Conflicts", "red", patterns, from_patterns, to_patterns); if (r < 0) return r; - r = graph_one_property(bus, u, "ConflictedBy", "red", patterns, from_patterns, to_patterns); - if (r < 0) - return r; } return 0; diff --git a/src/basic/af-list.c b/src/basic/af-list.c index f396115a34..07dfff6ad4 100644 --- a/src/basic/af-list.c +++ b/src/basic/af-list.c @@ -19,16 +19,16 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <sys/socket.h> #include <string.h> +#include <sys/socket.h> -#include "util.h" #include "af-list.h" +#include "util.h" static const struct af_name* lookup_af(register const char *str, register unsigned int len); -#include "af-to-name.h" #include "af-from-name.h" +#include "af-to-name.h" const char *af_to_name(int id) { diff --git a/src/basic/arphrd-list.c b/src/basic/arphrd-list.c index 284043cd90..03d8ad7403 100644 --- a/src/basic/arphrd-list.c +++ b/src/basic/arphrd-list.c @@ -22,13 +22,13 @@ #include <net/if_arp.h> #include <string.h> -#include "util.h" #include "arphrd-list.h" +#include "util.h" static const struct arphrd_name* lookup_arphrd(register const char *str, register unsigned int len); -#include "arphrd-to-name.h" #include "arphrd-from-name.h" +#include "arphrd-to-name.h" const char *arphrd_to_name(int id) { diff --git a/src/basic/async.c b/src/basic/async.c index c3135f0efe..cfc5d224e1 100644 --- a/src/basic/async.c +++ b/src/basic/async.c @@ -68,7 +68,7 @@ int asynchronous_sync(void) { } static void *close_thread(void *p) { - assert_se(close_nointr(PTR_TO_INT(p)) != -EBADF); + assert_se(close_nointr(PTR_TO_FD(p)) != -EBADF); return NULL; } @@ -84,7 +84,7 @@ int asynchronous_close(int fd) { if (fd >= 0) { PROTECT_ERRNO; - r = asynchronous_job(close_thread, INT_TO_PTR(fd)); + r = asynchronous_job(close_thread, FD_TO_PTR(fd)); if (r < 0) assert_se(close_nointr(fd) != -EBADF); } diff --git a/src/basic/audit-util.h b/src/basic/audit-util.h index 6de331c73e..026d3cd9b1 100644 --- a/src/basic/audit-util.h +++ b/src/basic/audit-util.h @@ -21,8 +21,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <stdint.h> #include <stdbool.h> +#include <stdint.h> #include <sys/types.h> #define AUDIT_SESSION_INVALID ((uint32_t) -1) diff --git a/src/basic/bitmap.h b/src/basic/bitmap.h index 2874bc99f7..9ce7b42d00 100644 --- a/src/basic/bitmap.h +++ b/src/basic/bitmap.h @@ -21,8 +21,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include "macro.h" #include "hashmap.h" +#include "macro.h" typedef struct Bitmap Bitmap; diff --git a/src/basic/btrfs-util.c b/src/basic/btrfs-util.c index 4c90bc0c80..be40dc5702 100644 --- a/src/basic/btrfs-util.c +++ b/src/basic/btrfs-util.c @@ -105,7 +105,7 @@ int btrfs_is_filesystem(int fd) { return F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC); } -int btrfs_is_subvol(int fd) { +int btrfs_is_subvol_fd(int fd) { struct stat st; assert(fd >= 0); @@ -121,6 +121,18 @@ int btrfs_is_subvol(int fd) { return btrfs_is_filesystem(fd); } +int btrfs_is_subvol(const char *path) { + _cleanup_close_ int fd = -1; + + assert(path); + + fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY); + if (fd < 0) + return -errno; + + return btrfs_is_subvol_fd(fd); +} + int btrfs_subvol_make(const char *path) { struct btrfs_ioctl_vol_args args = {}; _cleanup_close_ int fd = -1; @@ -575,8 +587,12 @@ int btrfs_qgroup_get_quota_fd(int fd, uint64_t qgroupid, BtrfsQuotaInfo *ret) { unsigned i; args.key.nr_items = 256; - if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0) + if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0) { + if (errno == ENOENT) /* quota tree is missing: quota disabled */ + break; + return -errno; + } if (args.key.nr_items <= 0) break; @@ -1006,6 +1022,10 @@ static int qgroup_create_or_destroy(int fd, bool b, uint64_t qgroupid) { for (c = 0;; c++) { if (ioctl(fd, BTRFS_IOC_QGROUP_CREATE, &args) < 0) { + /* If quota is not enabled, we get EINVAL. Turn this into a recognizable error */ + if (errno == EINVAL) + return -ENOPROTOOPT; + if (errno == EBUSY && c < 10) { (void) btrfs_quota_scan_wait(fd); continue; @@ -1335,8 +1355,12 @@ int btrfs_qgroup_copy_limits(int fd, uint64_t old_qgroupid, uint64_t new_qgroupi unsigned i; args.key.nr_items = 256; - if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0) + if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0) { + if (errno == ENOENT) /* quota tree missing: quota is not enabled, hence nothing to copy */ + break; + return -errno; + } if (args.key.nr_items <= 0) break; @@ -1411,12 +1435,16 @@ static int copy_quota_hierarchy(int fd, uint64_t old_subvol_id, uint64_t new_sub return n_old_qgroups; r = btrfs_subvol_get_parent(fd, old_subvol_id, &old_parent_id); - if (r < 0) + if (r == -ENXIO) + /* We have no parent, hence nothing to copy. */ + n_old_parent_qgroups = 0; + else if (r < 0) return r; - - n_old_parent_qgroups = btrfs_qgroup_find_parents(fd, old_parent_id, &old_parent_qgroups); - if (n_old_parent_qgroups < 0) - return n_old_parent_qgroups; + else { + n_old_parent_qgroups = btrfs_qgroup_find_parents(fd, old_parent_id, &old_parent_qgroups); + if (n_old_parent_qgroups < 0) + return n_old_parent_qgroups; + } for (i = 0; i < n_old_qgroups; i++) { uint64_t id; @@ -1670,7 +1698,7 @@ int btrfs_subvol_snapshot_fd(int old_fd, const char *new_path, BtrfsSnapshotFlag assert(old_fd >= 0); assert(new_path); - r = btrfs_is_subvol(old_fd); + r = btrfs_is_subvol_fd(old_fd); if (r < 0) return r; if (r == 0) { @@ -1766,8 +1794,12 @@ int btrfs_qgroup_find_parents(int fd, uint64_t qgroupid, uint64_t **ret) { unsigned i; args.key.nr_items = 256; - if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0) + if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0) { + if (errno == ENOENT) /* quota tree missing: quota is disabled */ + break; + return -errno; + } if (args.key.nr_items <= 0) break; @@ -1852,7 +1884,7 @@ int btrfs_subvol_auto_qgroup_fd(int fd, uint64_t subvol_id, bool insert_intermed */ if (subvol_id == 0) { - r = btrfs_is_subvol(fd); + r = btrfs_is_subvol_fd(fd); if (r < 0) return r; if (!r) @@ -1869,14 +1901,19 @@ int btrfs_subvol_auto_qgroup_fd(int fd, uint64_t subvol_id, bool insert_intermed if (n > 0) /* already parent qgroups set up, let's bail */ return 0; + qgroups = mfree(qgroups); + r = btrfs_subvol_get_parent(fd, subvol_id, &parent_subvol); - if (r < 0) + if (r == -ENXIO) + /* No parent, hence no qgroup memberships */ + n = 0; + else if (r < 0) return r; - - qgroups = mfree(qgroups); - n = btrfs_qgroup_find_parents(fd, parent_subvol, &qgroups); - if (n < 0) - return n; + else { + n = btrfs_qgroup_find_parents(fd, parent_subvol, &qgroups); + if (n < 0) + return n; + } if (insert_intermediary_qgroup) { uint64_t lowest = 256, new_qgroupid; diff --git a/src/basic/btrfs-util.h b/src/basic/btrfs-util.h index fc9efd72d5..8c11ce35d2 100644 --- a/src/basic/btrfs-util.h +++ b/src/basic/btrfs-util.h @@ -56,7 +56,9 @@ typedef enum BtrfsRemoveFlags { } BtrfsRemoveFlags; int btrfs_is_filesystem(int fd); -int btrfs_is_subvol(int fd); + +int btrfs_is_subvol_fd(int fd); +int btrfs_is_subvol(const char *path); int btrfs_reflink(int infd, int outfd); int btrfs_clone_range(int infd, uint64_t in_offset, int ofd, uint64_t out_offset, uint64_t sz); diff --git a/src/basic/calendarspec.c b/src/basic/calendarspec.c index 7151fc3d0c..8f60561ede 100644 --- a/src/basic/calendarspec.c +++ b/src/basic/calendarspec.c @@ -23,9 +23,10 @@ #include <string.h> #include "alloc-util.h" -#include "string-util.h" #include "calendarspec.h" #include "fileio.h" +#include "parse-util.h" +#include "string-util.h" #define BITS_WEEKDAYS 127 @@ -49,7 +50,7 @@ void calendar_spec_free(CalendarSpec *c) { free_chain(c->day); free_chain(c->hour); free_chain(c->minute); - free_chain(c->second); + free_chain(c->microsecond); free(c); } @@ -135,7 +136,7 @@ int calendar_spec_normalize(CalendarSpec *c) { sort_chain(&c->day); sort_chain(&c->hour); sort_chain(&c->minute); - sort_chain(&c->second); + sort_chain(&c->microsecond); return 0; } @@ -177,7 +178,7 @@ _pure_ bool calendar_spec_valid(CalendarSpec *c) { if (!chain_valid(c->minute, 0, 59)) return false; - if (!chain_valid(c->second, 0, 59)) + if (!chain_valid(c->microsecond, 0, 60*USEC_PER_SEC-1)) return false; return true; @@ -232,7 +233,7 @@ static void format_weekdays(FILE *f, const CalendarSpec *c) { } } -static void format_chain(FILE *f, int space, const CalendarComponent *c) { +static void format_chain(FILE *f, int space, const CalendarComponent *c, bool usec) { assert(f); if (!c) { @@ -241,14 +242,25 @@ static void format_chain(FILE *f, int space, const CalendarComponent *c) { } assert(c->value >= 0); - fprintf(f, "%0*i", space, c->value); - - if (c->repeat > 0) - fprintf(f, "/%i", c->repeat); + if (!usec) + fprintf(f, "%0*i", space, c->value); + else if (c->value % USEC_PER_SEC == 0) + fprintf(f, "%0*i", space, (int) (c->value / USEC_PER_SEC)); + else + fprintf(f, "%0*i.%06i", space, (int) (c->value / USEC_PER_SEC), (int) (c->value % USEC_PER_SEC)); + + if (c->repeat > 0) { + if (!usec) + fprintf(f, "/%i", c->repeat); + else if (c->repeat % USEC_PER_SEC == 0) + fprintf(f, "/%i", (int) (c->repeat / USEC_PER_SEC)); + else + fprintf(f, "/%i.%06i", (int) (c->repeat / USEC_PER_SEC), (int) (c->repeat % USEC_PER_SEC)); + } if (c->next) { fputc(',', f); - format_chain(f, space, c->next); + format_chain(f, space, c->next, usec); } } @@ -270,17 +282,17 @@ int calendar_spec_to_string(const CalendarSpec *c, char **p) { fputc(' ', f); } - format_chain(f, 4, c->year); + format_chain(f, 4, c->year, false); fputc('-', f); - format_chain(f, 2, c->month); + format_chain(f, 2, c->month, false); fputc('-', f); - format_chain(f, 2, c->day); + format_chain(f, 2, c->day, false); fputc(' ', f); - format_chain(f, 2, c->hour); + format_chain(f, 2, c->hour, false); fputc(':', f); - format_chain(f, 2, c->minute); + format_chain(f, 2, c->minute, false); fputc(':', f); - format_chain(f, 2, c->second); + format_chain(f, 2, c->microsecond, true); if (c->utc) fputs(" UTC", f); @@ -391,35 +403,70 @@ static int parse_weekdays(const char **p, CalendarSpec *c) { } } -static int prepend_component(const char **p, CalendarComponent **c) { - unsigned long value, repeat = 0; - char *e = NULL, *ee = NULL; - CalendarComponent *cc; - - assert(p); - assert(c); +static int parse_component_decimal(const char **p, bool usec, unsigned long *res) { + unsigned long value; + const char *e = NULL; + char *ee = NULL; + int r; errno = 0; - value = strtoul(*p, &e, 10); + value = strtoul(*p, &ee, 10); if (errno > 0) return -errno; - if (e == *p) + if (ee == *p) return -EINVAL; if ((unsigned long) (int) value != value) return -ERANGE; + e = ee; - if (*e == '/') { - repeat = strtoul(e+1, &ee, 10); - if (errno > 0) - return -errno; - if (ee == e+1) - return -EINVAL; - if ((unsigned long) (int) repeat != repeat) - return -ERANGE; - if (repeat <= 0) + if (usec) { + if (value * USEC_PER_SEC / USEC_PER_SEC != value) return -ERANGE; - e = ee; + value *= USEC_PER_SEC; + if (*e == '.') { + unsigned add; + + e++; + r = parse_fractional_part_u(&e, 6, &add); + if (r < 0) + return r; + + if (add + value < value) + return -ERANGE; + value += add; + } + } + + *p = e; + *res = value; + + return 0; +} + +static int prepend_component(const char **p, bool usec, CalendarComponent **c) { + unsigned long value, repeat = 0; + CalendarComponent *cc; + int r; + const char *e; + + assert(p); + assert(c); + + e = *p; + + r = parse_component_decimal(&e, usec, &value); + if (r < 0) + return r; + + if (*e == '/') { + e++; + r = parse_component_decimal(&e, usec, &repeat); + if (r < 0) + return r; + + if (repeat == 0) + return -ERANGE; } if (*e != 0 && *e != ' ' && *e != ',' && *e != '-' && *e != ':') @@ -438,13 +485,31 @@ static int prepend_component(const char **p, CalendarComponent **c) { if (*e ==',') { *p += 1; - return prepend_component(p, c); + return prepend_component(p, usec, c); } return 0; } -static int parse_chain(const char **p, CalendarComponent **c) { +static int const_chain(int value, CalendarComponent **c) { + CalendarComponent *cc = NULL; + + assert(c); + + cc = new0(CalendarComponent, 1); + if (!cc) + return -ENOMEM; + + cc->value = value; + cc->repeat = 0; + cc->next = *c; + + *c = cc; + + return 0; +} + +static int parse_chain(const char **p, bool usec, CalendarComponent **c) { const char *t; CalendarComponent *cc = NULL; int r; @@ -455,12 +520,19 @@ static int parse_chain(const char **p, CalendarComponent **c) { t = *p; if (t[0] == '*') { + if (usec) { + r = const_chain(0, c); + if (r < 0) + return r; + (*c)->repeat = USEC_PER_SEC; + } else + *c = NULL; + *p = t + 1; - *c = NULL; return 0; } - r = prepend_component(&t, &cc); + r = prepend_component(&t, usec, &cc); if (r < 0) { free_chain(cc); return r; @@ -471,24 +543,6 @@ static int parse_chain(const char **p, CalendarComponent **c) { return 0; } -static int const_chain(int value, CalendarComponent **c) { - CalendarComponent *cc = NULL; - - assert(c); - - cc = new0(CalendarComponent, 1); - if (!cc) - return -ENOMEM; - - cc->value = value; - cc->repeat = 0; - cc->next = *c; - - *c = cc; - - return 0; -} - static int parse_date(const char **p, CalendarSpec *c) { const char *t; int r; @@ -503,7 +557,7 @@ static int parse_date(const char **p, CalendarSpec *c) { if (*t == 0) return 0; - r = parse_chain(&t, &first); + r = parse_chain(&t, false, &first); if (r < 0) return r; @@ -519,7 +573,7 @@ static int parse_date(const char **p, CalendarSpec *c) { } t++; - r = parse_chain(&t, &second); + r = parse_chain(&t, false, &second); if (r < 0) { free_chain(first); return r; @@ -540,7 +594,7 @@ static int parse_date(const char **p, CalendarSpec *c) { } t++; - r = parse_chain(&t, &third); + r = parse_chain(&t, false, &third); if (r < 0) { free_chain(first); free_chain(second); @@ -582,7 +636,7 @@ static int parse_calendar_time(const char **p, CalendarSpec *c) { goto finish; } - r = parse_chain(&t, &h); + r = parse_chain(&t, false, &h); if (r < 0) goto fail; @@ -592,7 +646,7 @@ static int parse_calendar_time(const char **p, CalendarSpec *c) { } t++; - r = parse_chain(&t, &m); + r = parse_chain(&t, false, &m); if (r < 0) goto fail; @@ -610,7 +664,7 @@ static int parse_calendar_time(const char **p, CalendarSpec *c) { } t++; - r = parse_chain(&t, &s); + r = parse_chain(&t, true, &s); if (r < 0) goto fail; @@ -639,7 +693,8 @@ finish: *p = t; c->hour = h; c->minute = m; - c->second = s; + c->microsecond = s; + return 0; fail: @@ -671,7 +726,7 @@ int calendar_spec_from_string(const char *p, CalendarSpec **spec) { } if (strcaseeq(p, "minutely")) { - r = const_chain(0, &c->second); + r = const_chain(0, &c->microsecond); if (r < 0) goto fail; @@ -679,7 +734,7 @@ int calendar_spec_from_string(const char *p, CalendarSpec **spec) { r = const_chain(0, &c->minute); if (r < 0) goto fail; - r = const_chain(0, &c->second); + r = const_chain(0, &c->microsecond); if (r < 0) goto fail; @@ -690,7 +745,7 @@ int calendar_spec_from_string(const char *p, CalendarSpec **spec) { r = const_chain(0, &c->minute); if (r < 0) goto fail; - r = const_chain(0, &c->second); + r = const_chain(0, &c->microsecond); if (r < 0) goto fail; @@ -704,7 +759,7 @@ int calendar_spec_from_string(const char *p, CalendarSpec **spec) { r = const_chain(0, &c->minute); if (r < 0) goto fail; - r = const_chain(0, &c->second); + r = const_chain(0, &c->microsecond); if (r < 0) goto fail; @@ -724,7 +779,7 @@ int calendar_spec_from_string(const char *p, CalendarSpec **spec) { r = const_chain(0, &c->minute); if (r < 0) goto fail; - r = const_chain(0, &c->second); + r = const_chain(0, &c->microsecond); if (r < 0) goto fail; @@ -738,7 +793,7 @@ int calendar_spec_from_string(const char *p, CalendarSpec **spec) { r = const_chain(0, &c->minute); if (r < 0) goto fail; - r = const_chain(0, &c->second); + r = const_chain(0, &c->microsecond); if (r < 0) goto fail; @@ -765,7 +820,7 @@ int calendar_spec_from_string(const char *p, CalendarSpec **spec) { r = const_chain(0, &c->minute); if (r < 0) goto fail; - r = const_chain(0, &c->second); + r = const_chain(0, &c->microsecond); if (r < 0) goto fail; @@ -789,7 +844,7 @@ int calendar_spec_from_string(const char *p, CalendarSpec **spec) { r = const_chain(0, &c->minute); if (r < 0) goto fail; - r = const_chain(0, &c->second); + r = const_chain(0, &c->microsecond); if (r < 0) goto fail; @@ -906,14 +961,16 @@ static bool matches_weekday(int weekdays_bits, const struct tm *tm, bool utc) { return (weekdays_bits & (1 << k)); } -static int find_next(const CalendarSpec *spec, struct tm *tm) { +static int find_next(const CalendarSpec *spec, struct tm *tm, usec_t *usec) { struct tm c; + int tm_usec; int r; assert(spec); assert(tm); c = *tm; + tm_usec = *usec; for (;;) { /* Normalize the current date */ @@ -927,7 +984,7 @@ static int find_next(const CalendarSpec *spec, struct tm *tm) { if (r > 0) { c.tm_mon = 0; c.tm_mday = 1; - c.tm_hour = c.tm_min = c.tm_sec = 0; + c.tm_hour = c.tm_min = c.tm_sec = tm_usec = 0; } if (r < 0 || tm_out_of_bounds(&c, spec->utc)) return r; @@ -938,29 +995,29 @@ static int find_next(const CalendarSpec *spec, struct tm *tm) { if (r > 0) { c.tm_mday = 1; - c.tm_hour = c.tm_min = c.tm_sec = 0; + c.tm_hour = c.tm_min = c.tm_sec = tm_usec = 0; } if (r < 0 || tm_out_of_bounds(&c, spec->utc)) { c.tm_year ++; c.tm_mon = 0; c.tm_mday = 1; - c.tm_hour = c.tm_min = c.tm_sec = 0; + c.tm_hour = c.tm_min = c.tm_sec = tm_usec = 0; continue; } r = find_matching_component(spec->day, &c.tm_mday); if (r > 0) - c.tm_hour = c.tm_min = c.tm_sec = 0; + c.tm_hour = c.tm_min = c.tm_sec = tm_usec = 0; if (r < 0 || tm_out_of_bounds(&c, spec->utc)) { c.tm_mon ++; c.tm_mday = 1; - c.tm_hour = c.tm_min = c.tm_sec = 0; + c.tm_hour = c.tm_min = c.tm_sec = tm_usec = 0; continue; } if (!matches_weekday(spec->weekdays_bits, &c, spec->utc)) { c.tm_mday++; - c.tm_hour = c.tm_min = c.tm_sec = 0; + c.tm_hour = c.tm_min = c.tm_sec = tm_usec = 0; continue; } @@ -969,7 +1026,7 @@ static int find_next(const CalendarSpec *spec, struct tm *tm) { c.tm_min = c.tm_sec = 0; if (r < 0 || tm_out_of_bounds(&c, spec->utc)) { c.tm_mday ++; - c.tm_hour = c.tm_min = c.tm_sec = 0; + c.tm_hour = c.tm_min = c.tm_sec = tm_usec = 0; continue; } @@ -978,19 +1035,23 @@ static int find_next(const CalendarSpec *spec, struct tm *tm) { c.tm_sec = 0; if (r < 0 || tm_out_of_bounds(&c, spec->utc)) { c.tm_hour ++; - c.tm_min = c.tm_sec = 0; + c.tm_min = c.tm_sec = tm_usec = 0; continue; } - r = find_matching_component(spec->second, &c.tm_sec); + c.tm_sec = c.tm_sec * USEC_PER_SEC + tm_usec; + r = find_matching_component(spec->microsecond, &c.tm_sec); + tm_usec = c.tm_sec % USEC_PER_SEC; + c.tm_sec /= USEC_PER_SEC; + if (r < 0 || tm_out_of_bounds(&c, spec->utc)) { c.tm_min ++; - c.tm_sec = 0; + c.tm_sec = tm_usec = 0; continue; } - *tm = c; + *usec = tm_usec; return 0; } } @@ -999,14 +1060,17 @@ int calendar_spec_next_usec(const CalendarSpec *spec, usec_t usec, usec_t *next) struct tm tm; time_t t; int r; + usec_t tm_usec; assert(spec); assert(next); - t = (time_t) (usec / USEC_PER_SEC) + 1; + usec++; + t = (time_t) (usec / USEC_PER_SEC); assert_se(localtime_or_gmtime_r(&t, &tm, spec->utc)); + tm_usec = usec % USEC_PER_SEC; - r = find_next(spec, &tm); + r = find_next(spec, &tm, &tm_usec); if (r < 0) return r; @@ -1014,6 +1078,6 @@ int calendar_spec_next_usec(const CalendarSpec *spec, usec_t usec, usec_t *next) if (t == (time_t) -1) return -EINVAL; - *next = (usec_t) t * USEC_PER_SEC; + *next = (usec_t) t * USEC_PER_SEC + tm_usec; return 0; } diff --git a/src/basic/calendarspec.h b/src/basic/calendarspec.h index 56dc02f391..75b699682a 100644 --- a/src/basic/calendarspec.h +++ b/src/basic/calendarspec.h @@ -25,6 +25,7 @@ * time, a la cron */ #include <stdbool.h> + #include "util.h" typedef struct CalendarComponent { @@ -44,7 +45,7 @@ typedef struct CalendarSpec { CalendarComponent *hour; CalendarComponent *minute; - CalendarComponent *second; + CalendarComponent *microsecond; } CalendarSpec; void calendar_spec_free(CalendarSpec *c); diff --git a/src/basic/cap-list.c b/src/basic/cap-list.c index 4d391510bc..f0974900cd 100644 --- a/src/basic/cap-list.c +++ b/src/basic/cap-list.c @@ -28,8 +28,8 @@ static const struct capability_name* lookup_capability(register const char *str, register unsigned int len); -#include "cap-to-name.h" #include "cap-from-name.h" +#include "cap-to-name.h" const char *capability_to_name(int id) { diff --git a/src/basic/cgroup-util.h b/src/basic/cgroup-util.h index 01359fa7cb..a80ee60bd3 100644 --- a/src/basic/cgroup-util.h +++ b/src/basic/cgroup-util.h @@ -21,12 +21,12 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <sys/types.h> -#include <stdio.h> #include <dirent.h> +#include <stdio.h> +#include <sys/types.h> -#include "set.h" #include "def.h" +#include "set.h" /* An enum of well known cgroup controllers */ typedef enum CGroupController { diff --git a/src/basic/env-util.c b/src/basic/env-util.c index 9ddac5d6a1..441169db31 100644 --- a/src/basic/env-util.c +++ b/src/basic/env-util.c @@ -138,6 +138,21 @@ bool strv_env_is_valid(char **e) { return true; } +bool strv_env_name_is_valid(char **l) { + char **p, **q; + + STRV_FOREACH(p, l) { + if (!env_name_is_valid(*p)) + return false; + + STRV_FOREACH(q, p + 1) + if (streq(*p, *q)) + return false; + } + + return true; +} + bool strv_env_name_or_assignment_is_valid(char **l) { char **p, **q; diff --git a/src/basic/env-util.h b/src/basic/env-util.h index 6485dade18..5efffa3dc7 100644 --- a/src/basic/env-util.h +++ b/src/basic/env-util.h @@ -36,6 +36,7 @@ bool strv_env_is_valid(char **e); #define strv_env_clean(l) strv_env_clean_with_callback(l, NULL, NULL) char **strv_env_clean_with_callback(char **l, void (*invalid_callback)(const char *p, void *userdata), void *userdata); +bool strv_env_name_is_valid(char **l); bool strv_env_name_or_assignment_is_valid(char **l); char **strv_env_merge(unsigned n_lists, ...); diff --git a/src/basic/errno-list.c b/src/basic/errno-list.c index 34d1331486..22869e4136 100644 --- a/src/basic/errno-list.c +++ b/src/basic/errno-list.c @@ -21,14 +21,14 @@ #include <string.h> -#include "util.h" #include "errno-list.h" +#include "util.h" static const struct errno_name* lookup_errno(register const char *str, register unsigned int len); -#include "errno-to-name.h" #include "errno-from-name.h" +#include "errno-to-name.h" const char *errno_to_name(int id) { diff --git a/src/basic/escape.c b/src/basic/escape.c index 4815161b09..42a84c9317 100644 --- a/src/basic/escape.c +++ b/src/basic/escape.c @@ -89,20 +89,20 @@ size_t cescape_char(char c, char *buf) { return buf - buf_old; } -char *cescape(const char *s) { - char *r, *t; +char *cescape_length(const char *s, size_t n) { const char *f; + char *r, *t; - assert(s); + assert(s || n == 0); /* Does C style string escaping. May be reversed with * cunescape(). */ - r = new(char, strlen(s)*4 + 1); + r = new(char, n*4 + 1); if (!r) return NULL; - for (f = s, t = r; *f; f++) + for (f = s, t = r; f < s + n; f++) t += cescape_char(*f, t); *t = 0; @@ -110,6 +110,12 @@ char *cescape(const char *s) { return r; } +char *cescape(const char *s) { + assert(s); + + return cescape_length(s, strlen(s)); +} + int cunescape_one(const char *p, size_t length, char *ret, uint32_t *ret_unicode) { int r = 1; diff --git a/src/basic/escape.h b/src/basic/escape.h index 85ba909081..52ebf11c4a 100644 --- a/src/basic/escape.h +++ b/src/basic/escape.h @@ -21,8 +21,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <sys/types.h> #include <inttypes.h> +#include <sys/types.h> /* What characters are special in the shell? */ /* must be escaped outside and inside double-quotes */ @@ -35,6 +35,7 @@ typedef enum UnescapeFlags { } UnescapeFlags; char *cescape(const char *s); +char *cescape_length(const char *s, size_t n); size_t cescape_char(char c, char *buf); int cunescape(const char *s, UnescapeFlags flags, char **ret); diff --git a/src/basic/exit-status.c b/src/basic/exit-status.c index fcff753ada..4c83731540 100644 --- a/src/basic/exit-status.c +++ b/src/basic/exit-status.c @@ -19,12 +19,12 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <stdlib.h> #include <signal.h> +#include <stdlib.h> #include "exit-status.h" -#include "set.h" #include "macro.h" +#include "set.h" const char* exit_status_to_string(ExitStatus status, ExitStatusLevel level) { diff --git a/src/basic/extract-word.c b/src/basic/extract-word.c index ff6d211ef4..fd495692fa 100644 --- a/src/basic/extract-word.c +++ b/src/basic/extract-word.c @@ -128,7 +128,7 @@ int extract_first_word(const char **p, char **ret, const char *separators, Extra } else if (c == quote) { /* found the end quote */ quote = 0; break; - } else if (c == '\\') { + } else if (c == '\\' && !(flags & EXTRACT_RETAIN_ESCAPE)) { backslash = true; break; } else { @@ -146,7 +146,7 @@ int extract_first_word(const char **p, char **ret, const char *separators, Extra else if ((c == '\'' || c == '"') && (flags & EXTRACT_QUOTES)) { quote = c; break; - } else if (c == '\\') { + } else if (c == '\\' && !(flags & EXTRACT_RETAIN_ESCAPE)) { backslash = true; break; } else if (strchr(separators, c)) { diff --git a/src/basic/extract-word.h b/src/basic/extract-word.h index ddc1c4f463..9606ab64b3 100644 --- a/src/basic/extract-word.h +++ b/src/basic/extract-word.h @@ -24,11 +24,12 @@ #include "macro.h" typedef enum ExtractFlags { - EXTRACT_RELAX = 1, - EXTRACT_CUNESCAPE = 2, - EXTRACT_CUNESCAPE_RELAX = 4, - EXTRACT_QUOTES = 8, + EXTRACT_RELAX = 1, + EXTRACT_CUNESCAPE = 2, + EXTRACT_CUNESCAPE_RELAX = 4, + EXTRACT_QUOTES = 8, EXTRACT_DONT_COALESCE_SEPARATORS = 16, + EXTRACT_RETAIN_ESCAPE = 32, } ExtractFlags; int extract_first_word(const char **p, char **ret, const char *separators, ExtractFlags flags); diff --git a/src/basic/fd-util.h b/src/basic/fd-util.h index 1ca10f0383..5ce1592eeb 100644 --- a/src/basic/fd-util.h +++ b/src/basic/fd-util.h @@ -21,13 +21,17 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <stdio.h> #include <dirent.h> #include <stdbool.h> +#include <stdio.h> #include <sys/socket.h> #include "macro.h" +/* Make sure we can distinguish fd 0 and NULL */ +#define FD_TO_PTR(fd) INT_TO_PTR((fd)+1) +#define PTR_TO_FD(p) (PTR_TO_INT(p)-1) + int close_nointr(int fd); int safe_close(int fd); void safe_close_pair(int p[]); diff --git a/src/basic/fdset.c b/src/basic/fdset.c index 42b0b2b98f..e5452f3bb0 100644 --- a/src/basic/fdset.c +++ b/src/basic/fdset.c @@ -19,8 +19,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <errno.h> #include <dirent.h> +#include <errno.h> #include <fcntl.h> #include "sd-daemon.h" @@ -36,10 +36,6 @@ #define MAKE_SET(s) ((Set*) s) #define MAKE_FDSET(s) ((FDSet*) s) -/* Make sure we can distinguish fd 0 and NULL */ -#define FD_TO_PTR(fd) INT_TO_PTR((fd)+1) -#define PTR_TO_FD(p) (PTR_TO_INT(p)-1) - FDSet *fdset_new(void) { return MAKE_FDSET(set_new(NULL)); } diff --git a/src/basic/fileio-label.c b/src/basic/fileio-label.c index f596f1d11f..0405822ce0 100644 --- a/src/basic/fileio-label.c +++ b/src/basic/fileio-label.c @@ -20,9 +20,9 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include "util.h" -#include "selinux-util.h" #include "fileio-label.h" +#include "selinux-util.h" +#include "util.h" int write_string_file_atomic_label(const char *fn, const char *line) { int r; diff --git a/src/basic/fileio-label.h b/src/basic/fileio-label.h index 25fa351be2..9feb3cccb5 100644 --- a/src/basic/fileio-label.h +++ b/src/basic/fileio-label.h @@ -23,6 +23,7 @@ ***/ #include <stdio.h> + #include "fileio.h" int write_string_file_atomic_label(const char *fn, const char *line); diff --git a/src/basic/fileio.c b/src/basic/fileio.c index 619dafb517..10aacdc56d 100644 --- a/src/basic/fileio.c +++ b/src/basic/fileio.c @@ -28,8 +28,10 @@ #include "fileio.h" #include "fs-util.h" #include "hexdecoct.h" +#include "parse-util.h" #include "path-util.h" #include "random-util.h" +#include "stdio-util.h" #include "string-util.h" #include "strv.h" #include "umask-util.h" @@ -76,6 +78,7 @@ static int write_string_file_atomic(const char *fn, const char *line, bool enfor int write_string_file(const char *fn, const char *line, WriteStringFileFlags flags) { _cleanup_fclose_ FILE *f = NULL; + int q, r; assert(fn); assert(line); @@ -83,30 +86,58 @@ int write_string_file(const char *fn, const char *line, WriteStringFileFlags fla if (flags & WRITE_STRING_FILE_ATOMIC) { assert(flags & WRITE_STRING_FILE_CREATE); - return write_string_file_atomic(fn, line, !(flags & WRITE_STRING_FILE_AVOID_NEWLINE)); + r = write_string_file_atomic(fn, line, !(flags & WRITE_STRING_FILE_AVOID_NEWLINE)); + if (r < 0) + goto fail; + + return r; } if (flags & WRITE_STRING_FILE_CREATE) { f = fopen(fn, "we"); - if (!f) - return -errno; + if (!f) { + r = -errno; + goto fail; + } } else { int fd; /* We manually build our own version of fopen(..., "we") that * works without O_CREAT */ fd = open(fn, O_WRONLY|O_CLOEXEC|O_NOCTTY); - if (fd < 0) - return -errno; + if (fd < 0) { + r = -errno; + goto fail; + } f = fdopen(fd, "we"); if (!f) { + r = -errno; safe_close(fd); - return -errno; + goto fail; } } - return write_string_stream(f, line, !(flags & WRITE_STRING_FILE_AVOID_NEWLINE)); + r = write_string_stream(f, line, !(flags & WRITE_STRING_FILE_AVOID_NEWLINE)); + if (r < 0) + goto fail; + + return 0; + +fail: + if (!(flags & WRITE_STRING_FILE_VERIFY_ON_FAILURE)) + return r; + + f = safe_fclose(f); + + /* OK, the operation failed, but let's see if the right + * contents in place already. If so, eat up the error. */ + + q = verify_file(fn, line, !(flags & WRITE_STRING_FILE_AVOID_NEWLINE)); + if (q <= 0) + return r; + + return 0; } int read_one_line_file(const char *fn, char **line) { @@ -137,15 +168,41 @@ int read_one_line_file(const char *fn, char **line) { return 0; } -int verify_one_line_file(const char *fn, const char *line) { - _cleanup_free_ char *value = NULL; - int r; +int verify_file(const char *fn, const char *blob, bool accept_extra_nl) { + _cleanup_fclose_ FILE *f = NULL; + _cleanup_free_ char *buf = NULL; + size_t l, k; - r = read_one_line_file(fn, &value); - if (r < 0) - return r; + assert(fn); + assert(blob); - return streq(value, line); + l = strlen(blob); + + if (accept_extra_nl && endswith(blob, "\n")) + accept_extra_nl = false; + + buf = malloc(l + accept_extra_nl + 1); + if (!buf) + return -ENOMEM; + + f = fopen(fn, "re"); + if (!f) + return -errno; + + /* We try to read one byte more than we need, so that we know whether we hit eof */ + errno = 0; + k = fread(buf, 1, l + accept_extra_nl + 1, f); + if (ferror(f)) + return errno > 0 ? -errno : -EIO; + + if (k != l && k != l + accept_extra_nl) + return 0; + if (memcmp(buf, blob, l) != 0) + return 0; + if (k > l && buf[l] != '\n') + return 0; + + return 1; } int read_full_stream(FILE *f, char **contents, size_t *size) { @@ -1149,3 +1206,37 @@ int tempfn_random_child(const char *p, const char *extra, char **ret) { *ret = path_kill_slashes(t); return 0; } + +int write_timestamp_file_atomic(const char *fn, usec_t n) { + char ln[DECIMAL_STR_MAX(n)+2]; + + /* Creates a "timestamp" file, that contains nothing but a + * usec_t timestamp, formatted in ASCII. */ + + if (n <= 0 || n >= USEC_INFINITY) + return -ERANGE; + + xsprintf(ln, USEC_FMT "\n", n); + + return write_string_file(fn, ln, WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC); +} + +int read_timestamp_file(const char *fn, usec_t *ret) { + _cleanup_free_ char *ln = NULL; + uint64_t t; + int r; + + r = read_one_line_file(fn, &ln); + if (r < 0) + return r; + + r = safe_atou64(ln, &t); + if (r < 0) + return r; + + if (t <= 0 || t >= (uint64_t) USEC_INFINITY) + return -ERANGE; + + *ret = (usec_t) t; + return 0; +} diff --git a/src/basic/fileio.h b/src/basic/fileio.h index fa7f192331..95e8698941 100644 --- a/src/basic/fileio.h +++ b/src/basic/fileio.h @@ -28,11 +28,13 @@ #include <sys/types.h> #include "macro.h" +#include "time-util.h" typedef enum { WRITE_STRING_FILE_CREATE = 1, WRITE_STRING_FILE_ATOMIC = 2, WRITE_STRING_FILE_AVOID_NEWLINE = 4, + WRITE_STRING_FILE_VERIFY_ON_FAILURE = 8, } WriteStringFileFlags; int write_string_stream(FILE *f, const char *line, bool enforce_newline); @@ -42,7 +44,7 @@ int read_one_line_file(const char *fn, char **line); int read_full_file(const char *fn, char **contents, size_t *size); int read_full_stream(FILE *f, char **contents, size_t *size); -int verify_one_line_file(const char *fn, const char *line); +int verify_file(const char *fn, const char *blob, bool accept_extra_nl); int parse_env_file(const char *fname, const char *separator, ...) _sentinel_; int load_env_file(FILE *f, const char *fname, const char *separator, char ***l); @@ -77,3 +79,6 @@ int open_tmpfile(const char *path, int flags); int tempfn_xxxxxx(const char *p, const char *extra, char **ret); int tempfn_random(const char *p, const char *extra, char **ret); int tempfn_random_child(const char *p, const char *extra, char **ret); + +int write_timestamp_file_atomic(const char *fn, usec_t n); +int read_timestamp_file(const char *fn, usec_t *ret); diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c index 7aee404bfc..2b6189ad90 100644 --- a/src/basic/fs-util.c +++ b/src/basic/fs-util.c @@ -233,6 +233,26 @@ int readlink_and_canonicalize(const char *p, char **r) { return 0; } +int readlink_and_make_absolute_root(const char *root, const char *path, char **ret) { + _cleanup_free_ char *target = NULL, *t = NULL; + const char *full; + int r; + + full = prefix_roota(root, path); + r = readlink_malloc(full, &target); + if (r < 0) + return r; + + t = file_in_same_dir(path, target); + if (!t) + return -ENOMEM; + + *ret = t; + t = NULL; + + return 0; +} + int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) { assert(path); @@ -311,7 +331,7 @@ int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gi if (fd < 0) return -errno; - if (mode > 0) { + if (mode != MODE_INVALID) { r = fchmod(fd, mode); if (r < 0) return -errno; @@ -338,7 +358,7 @@ int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gi } int touch(const char *path) { - return touch_file(path, false, USEC_INFINITY, UID_INVALID, GID_INVALID, 0); + return touch_file(path, false, USEC_INFINITY, UID_INVALID, GID_INVALID, MODE_INVALID); } int symlink_idempotent(const char *from, const char *to) { diff --git a/src/basic/fs-util.h b/src/basic/fs-util.h index b94873e65b..5fbb7bc4c3 100644 --- a/src/basic/fs-util.h +++ b/src/basic/fs-util.h @@ -22,10 +22,10 @@ ***/ #include <fcntl.h> +#include <limits.h> #include <sys/inotify.h> #include <sys/types.h> #include <unistd.h> -#include <limits.h> #include "time-util.h" @@ -40,6 +40,7 @@ int readlink_malloc(const char *p, char **r); int readlink_value(const char *p, char **ret); int readlink_and_make_absolute(const char *p, char **r); int readlink_and_canonicalize(const char *p, char **r); +int readlink_and_make_absolute_root(const char *root, const char *path, char **ret); int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid); int fchmod_and_fchown(int fd, mode_t mode, uid_t uid, gid_t gid); diff --git a/src/basic/gunicode.h b/src/basic/gunicode.h index e70818fdd7..b03aa43160 100644 --- a/src/basic/gunicode.h +++ b/src/basic/gunicode.h @@ -6,8 +6,8 @@ #pragma once -#include <stdint.h> #include <stdbool.h> +#include <stdint.h> #include <stdlib.h> char *utf8_prev_char (const char *p); diff --git a/src/basic/hashmap.c b/src/basic/hashmap.c index 4109a08c6c..6e501ef6ff 100644 --- a/src/basic/hashmap.c +++ b/src/basic/hashmap.c @@ -380,7 +380,7 @@ static unsigned base_bucket_hash(HashmapBase *h, const void *p) { h->hash_ops->hash(p, &state); - siphash24_finalize((uint8_t*)&hash, &state); + hash = siphash24_finalize(&state); return (unsigned) (hash % n_buckets(h)); } diff --git a/src/basic/hostname-util.c b/src/basic/hostname-util.c index ea0528c6fc..c57a3cbd60 100644 --- a/src/basic/hostname-util.c +++ b/src/basic/hostname-util.c @@ -72,7 +72,7 @@ static bool hostname_valid_char(char c) { * allow_trailing_dot is true and at least two components are present * in the name. Note that due to the restricted charset and length * this call is substantially more conservative than - * dns_domain_is_valid(). + * dns_name_is_valid(). */ bool hostname_is_valid(const char *s, bool allow_trailing_dot) { unsigned n_dots = 0; diff --git a/src/basic/in-addr-util.c b/src/basic/in-addr-util.c index f4e24121e7..b75c39aac7 100644 --- a/src/basic/in-addr-util.c +++ b/src/basic/in-addr-util.c @@ -44,7 +44,7 @@ int in_addr_is_link_local(int family, const union in_addr_union *u) { assert(u); if (family == AF_INET) - return (be32toh(u->in.s_addr) & 0xFFFF0000) == (169U << 24 | 254U << 16); + return (be32toh(u->in.s_addr) & UINT32_C(0xFFFF0000)) == (UINT32_C(169) << 24 | UINT32_C(254) << 16); if (family == AF_INET6) return IN6_IS_ADDR_LINKLOCAL(&u->in6); @@ -52,6 +52,19 @@ int in_addr_is_link_local(int family, const union in_addr_union *u) { return -EAFNOSUPPORT; } +int in_addr_is_localhost(int family, const union in_addr_union *u) { + assert(u); + + if (family == AF_INET) + /* All of 127.x.x.x is localhost. */ + return (be32toh(u->in.s_addr) & UINT32_C(0xFF000000)) == UINT32_C(127) << 24; + + if (family == AF_INET6) + return IN6_IS_ADDR_LOOPBACK(&u->in6); + + return -EAFNOSUPPORT; +} + int in_addr_equal(int family, const union in_addr_union *a, const union in_addr_union *b) { assert(a); assert(b); diff --git a/src/basic/in-addr-util.h b/src/basic/in-addr-util.h index 51af08868c..58f55b3418 100644 --- a/src/basic/in-addr-util.h +++ b/src/basic/in-addr-util.h @@ -33,6 +33,7 @@ union in_addr_union { int in_addr_is_null(int family, const union in_addr_union *u); int in_addr_is_link_local(int family, const union in_addr_union *u); +int in_addr_is_localhost(int family, const union in_addr_union *u); int in_addr_equal(int family, const union in_addr_union *a, const union in_addr_union *b); int in_addr_prefix_intersect(int family, const union in_addr_union *a, unsigned aprefixlen, const union in_addr_union *b, unsigned bprefixlen); int in_addr_prefix_next(int family, union in_addr_union *u, unsigned prefixlen); diff --git a/src/basic/ioprio.h b/src/basic/ioprio.h index e5c71d0043..d8bb6eb497 100644 --- a/src/basic/ioprio.h +++ b/src/basic/ioprio.h @@ -4,8 +4,8 @@ /* This is minimal version of Linux' linux/ioprio.h header file, which * is licensed GPL2 */ -#include <unistd.h> #include <sys/syscall.h> +#include <unistd.h> /* * Gives us 8 prio classes with 13-bits of data for each class diff --git a/src/basic/json.c b/src/basic/json.c index 716705e5ff..9d5dedb934 100644 --- a/src/basic/json.c +++ b/src/basic/json.c @@ -23,9 +23,9 @@ #include <sys/types.h> #include "alloc-util.h" +#include "hexdecoct.h" #include "json.h" #include "macro.h" -#include "hexdecoct.h" #include "string-util.h" #include "utf8.h" diff --git a/src/basic/json.h b/src/basic/json.h index e0b4d810b5..8a7d79cb17 100644 --- a/src/basic/json.h +++ b/src/basic/json.h @@ -22,6 +22,7 @@ ***/ #include <stdbool.h> + #include "util.h" enum { diff --git a/src/basic/label.c b/src/basic/label.c index 82f10b21bd..f33502f90f 100644 --- a/src/basic/label.c +++ b/src/basic/label.c @@ -19,10 +19,10 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include "label.h" #include "selinux-util.h" #include "smack-util.h" #include "util.h" -#include "label.h" int label_fix(const char *path, bool ignore_enoent, bool ignore_erofs) { int r, q; diff --git a/src/basic/lockfile-util.c b/src/basic/lockfile-util.c index 87c3aef7af..0bdbae480b 100644 --- a/src/basic/lockfile-util.c +++ b/src/basic/lockfile-util.c @@ -19,12 +19,12 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <stdlib.h> -#include <stdbool.h> #include <errno.h> -#include <string.h> -#include <stdio.h> #include <limits.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> #include <sys/file.h> #include "alloc-util.h" diff --git a/src/basic/login-util.c b/src/basic/login-util.c index 832f477bd2..41cef14e73 100644 --- a/src/basic/login-util.c +++ b/src/basic/login-util.c @@ -20,8 +20,8 @@ ***/ #include "def.h" -#include "string-util.h" #include "login-util.h" +#include "string-util.h" bool session_id_valid(const char *id) { diff --git a/src/basic/memfd-util.h b/src/basic/memfd-util.h index 2cb404ea81..3e4de008a4 100644 --- a/src/basic/memfd-util.h +++ b/src/basic/memfd-util.h @@ -21,8 +21,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <sys/types.h> #include <inttypes.h> +#include <sys/types.h> int memfd_new(const char *name); int memfd_new_and_map(const char *name, size_t sz, void **p); diff --git a/src/basic/mempool.c b/src/basic/mempool.c index d5d98d8829..9ee6e6a76d 100644 --- a/src/basic/mempool.c +++ b/src/basic/mempool.c @@ -20,8 +20,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include "mempool.h" #include "macro.h" +#include "mempool.h" #include "util.h" struct pool { diff --git a/src/basic/missing.h b/src/basic/missing.h index 306c56a156..d539ed00e4 100644 --- a/src/basic/missing.h +++ b/src/basic/missing.h @@ -23,19 +23,20 @@ /* Missing glibc definitions to access certain kernel APIs */ -#include <sys/resource.h> -#include <sys/syscall.h> -#include <fcntl.h> -#include <stdlib.h> -#include <unistd.h> #include <errno.h> -#include <linux/oom.h> -#include <linux/input.h> -#include <linux/if_link.h> -#include <linux/loop.h> +#include <fcntl.h> #include <linux/audit.h> #include <linux/capability.h> +#include <linux/if_link.h> +#include <linux/input.h> +#include <linux/loop.h> #include <linux/neighbour.h> +#include <linux/oom.h> +#include <linux/rtnetlink.h> +#include <stdlib.h> +#include <sys/resource.h> +#include <sys/syscall.h> +#include <unistd.h> #ifdef HAVE_AUDIT #include <libaudit.h> @@ -900,6 +901,10 @@ static inline int setns(int fd, int nstype) { #define NDA_MAX (__NDA_MAX - 1) #endif +#ifndef RTA_PREF +#define RTA_PREF 20 +#endif + #ifndef IPV6_UNICAST_IF #define IPV6_UNICAST_IF 76 #endif diff --git a/src/basic/mkdir-label.c b/src/basic/mkdir-label.c index 76bbc1edda..c241ef6064 100644 --- a/src/basic/mkdir-label.c +++ b/src/basic/mkdir-label.c @@ -20,8 +20,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <unistd.h> #include <stdio.h> +#include <unistd.h> #include "label.h" #include "mkdir.h" diff --git a/src/basic/mkdir.c b/src/basic/mkdir.c index 0214c4627e..5d7fb9a12d 100644 --- a/src/basic/mkdir.c +++ b/src/basic/mkdir.c @@ -19,8 +19,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <string.h> #include <errno.h> +#include <string.h> #include "fs-util.h" #include "mkdir.h" diff --git a/src/basic/ordered-set.h b/src/basic/ordered-set.h index 6c617ab305..da10e90ff2 100644 --- a/src/basic/ordered-set.h +++ b/src/basic/ordered-set.h @@ -29,6 +29,17 @@ static inline OrderedSet* ordered_set_new(const struct hash_ops *ops) { return (OrderedSet*) ordered_hashmap_new(ops); } +static inline int ordered_set_ensure_allocated(OrderedSet **s, const struct hash_ops *ops) { + if (*s) + return 0; + + *s = ordered_set_new(ops); + if (!*s) + return -ENOMEM; + + return 0; +} + static inline OrderedSet* ordered_set_free(OrderedSet *s) { ordered_hashmap_free((OrderedHashmap*) s); return NULL; diff --git a/src/basic/parse-util.c b/src/basic/parse-util.c index 151067e916..3ae99d9334 100644 --- a/src/basic/parse-util.c +++ b/src/basic/parse-util.c @@ -490,3 +490,39 @@ int safe_atod(const char *s, double *ret_d) { *ret_d = (double) d; return 0; } + +int parse_fractional_part_u(const char **p, size_t digits, unsigned *res) { + size_t i; + unsigned val = 0; + const char *s; + + s = *p; + + /* accept any number of digits, strtoull is limted to 19 */ + for(i=0; i < digits; i++,s++) { + if (*s < '0' || *s > '9') { + if (i == 0) + return -EINVAL; + + /* too few digits, pad with 0 */ + for (; i < digits; i++) + val *= 10; + + break; + } + + val *= 10; + val += *s - '0'; + } + + /* maybe round up */ + if (*s >= '5' && *s <= '9') + val++; + + s += strspn(s, DIGITS); + + *p = s; + *res = val; + + return 0; +} diff --git a/src/basic/parse-util.h b/src/basic/parse-util.h index 408690d0b3..125de53d7a 100644 --- a/src/basic/parse-util.h +++ b/src/basic/parse-util.h @@ -90,3 +90,5 @@ static inline int safe_atoli(const char *s, long int *ret_u) { #endif int safe_atod(const char *s, double *ret_d); + +int parse_fractional_part_u(const char **s, size_t digits, unsigned *res); diff --git a/src/basic/process-util.h b/src/basic/process-util.h index 72633ebf70..fdc7e1bdef 100644 --- a/src/basic/process-util.h +++ b/src/basic/process-util.h @@ -19,12 +19,12 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <stdbool.h> -#include <sys/types.h> #include <alloca.h> +#include <signal.h> +#include <stdbool.h> #include <stdio.h> #include <string.h> -#include <signal.h> +#include <sys/types.h> #include "formats-util.h" #include "macro.h" diff --git a/src/basic/selinux-util.c b/src/basic/selinux-util.c index a821a3d5bb..e8ce5cfd96 100644 --- a/src/basic/selinux-util.c +++ b/src/basic/selinux-util.c @@ -24,15 +24,15 @@ #include <sys/un.h> #ifdef HAVE_SELINUX -#include <selinux/selinux.h> -#include <selinux/label.h> #include <selinux/context.h> +#include <selinux/label.h> +#include <selinux/selinux.h> #endif #include "alloc-util.h" -#include "strv.h" #include "path-util.h" #include "selinux-util.h" +#include "strv.h" #ifdef HAVE_SELINUX DEFINE_TRIVIAL_CLEANUP_FUNC(security_context_t, freecon); diff --git a/src/basic/selinux-util.h b/src/basic/selinux-util.h index 2afcaec183..d19984c5fe 100644 --- a/src/basic/selinux-util.h +++ b/src/basic/selinux-util.h @@ -21,8 +21,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <sys/socket.h> #include <stdbool.h> +#include <sys/socket.h> #include "macro.h" diff --git a/src/basic/set.h b/src/basic/set.h index 4554ef2d49..5fd7de08f9 100644 --- a/src/basic/set.h +++ b/src/basic/set.h @@ -27,7 +27,6 @@ Set *internal_set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); #define set_new(ops) internal_set_new(ops HASHMAP_DEBUG_SRC_ARGS) - static inline Set *set_free(Set *s) { internal_hashmap_free(HASHMAP_BASE(s)); return NULL; diff --git a/src/basic/sigbus.c b/src/basic/sigbus.c index 0108603fe8..c535c89d52 100644 --- a/src/basic/sigbus.c +++ b/src/basic/sigbus.c @@ -23,8 +23,8 @@ #include <sys/mman.h> #include "macro.h" -#include "util.h" #include "sigbus.h" +#include "util.h" #define SIGBUS_QUEUE_MAX 64 diff --git a/src/basic/siphash24.c b/src/basic/siphash24.c index 3b61961389..10fc56da69 100644 --- a/src/basic/siphash24.c +++ b/src/basic/siphash24.c @@ -17,9 +17,9 @@ coding style) */ -#include "sparse-endian.h" - #include "siphash24.h" +#include "sparse-endian.h" +#include "unaligned.h" #include "util.h" static inline uint64_t rotate_left(uint64_t x, uint8_t b) { @@ -53,37 +53,40 @@ void siphash24_init(struct siphash *state, const uint8_t k[16]) { assert(state); assert(k); - k0 = le64toh(*(le64_t*) k); - k1 = le64toh(*(le64_t*) (k + 8)); - - /* "somepseudorandomlygeneratedbytes" */ - state->v0 = 0x736f6d6570736575ULL ^ k0; - state->v1 = 0x646f72616e646f6dULL ^ k1; - state->v2 = 0x6c7967656e657261ULL ^ k0; - state->v3 = 0x7465646279746573ULL ^ k1; - state->padding = 0; - state->inlen = 0; + k0 = unaligned_read_le64(k); + k1 = unaligned_read_le64(k + 8); + + *state = (struct siphash) { + /* "somepseudorandomlygeneratedbytes" */ + .v0 = 0x736f6d6570736575ULL ^ k0, + .v1 = 0x646f72616e646f6dULL ^ k1, + .v2 = 0x6c7967656e657261ULL ^ k0, + .v3 = 0x7465646279746573ULL ^ k1, + .padding = 0, + .inlen = 0, + }; } void siphash24_compress(const void *_in, size_t inlen, struct siphash *state) { - uint64_t m; + const uint8_t *in = _in; const uint8_t *end = in + inlen; - unsigned left = state->inlen & 7; + size_t left = state->inlen & 7; + uint64_t m; assert(in); assert(state); - /* update total length */ + /* Update total length */ state->inlen += inlen; - /* if padding exists, fill it out */ + /* If padding exists, fill it out */ if (left > 0) { - for ( ; in < end && left < 8; in ++, left ++ ) - state->padding |= ( ( uint64_t )*in ) << (left * 8); + for ( ; in < end && left < 8; in ++, left ++) + state->padding |= ((uint64_t) *in) << (left * 8); if (in == end && left < 8) - /* we did not have enough input to fill out the padding completely */ + /* We did not have enough input to fill out the padding completely */ return; #ifdef DEBUG @@ -93,6 +96,7 @@ void siphash24_compress(const void *_in, size_t inlen, struct siphash *state) { printf("(%3zu) v3 %08x %08x\n", state->inlen, (uint32_t) (state->v3 >> 32), (uint32_t) state->v3); printf("(%3zu) compress padding %08x %08x\n", state->inlen, (uint32_t) (state->padding >> 32), (uint32_t)state->padding); #endif + state->v3 ^= state->padding; sipround(state); sipround(state); @@ -101,10 +105,10 @@ void siphash24_compress(const void *_in, size_t inlen, struct siphash *state) { state->padding = 0; } - end -= ( state->inlen % sizeof (uint64_t) ); + end -= (state->inlen % sizeof(uint64_t)); - for ( ; in < end; in += 8 ) { - m = le64toh(*(le64_t*) in); + for ( ; in < end; in += 8) { + m = unaligned_read_le64(in); #ifdef DEBUG printf("(%3zu) v0 %08x %08x\n", state->inlen, (uint32_t) (state->v0 >> 32), (uint32_t) state->v0); printf("(%3zu) v1 %08x %08x\n", state->inlen, (uint32_t) (state->v1 >> 32), (uint32_t) state->v1); @@ -119,38 +123,41 @@ void siphash24_compress(const void *_in, size_t inlen, struct siphash *state) { } left = state->inlen & 7; - - switch(left) - { - case 7: state->padding |= ((uint64_t) in[6]) << 48; - - case 6: state->padding |= ((uint64_t) in[5]) << 40; - - case 5: state->padding |= ((uint64_t) in[4]) << 32; - - case 4: state->padding |= ((uint64_t) in[3]) << 24; - - case 3: state->padding |= ((uint64_t) in[2]) << 16; - - case 2: state->padding |= ((uint64_t) in[1]) << 8; - - case 1: state->padding |= ((uint64_t) in[0]); break; - - case 0: break; + switch (left) { + case 7: + state->padding |= ((uint64_t) in[6]) << 48; + case 6: + state->padding |= ((uint64_t) in[5]) << 40; + case 5: + state->padding |= ((uint64_t) in[4]) << 32; + case 4: + state->padding |= ((uint64_t) in[3]) << 24; + case 3: + state->padding |= ((uint64_t) in[2]) << 16; + case 2: + state->padding |= ((uint64_t) in[1]) << 8; + case 1: + state->padding |= ((uint64_t) in[0]); + case 0: + break; } } -void siphash24_finalize(uint8_t out[8], struct siphash *state) { +uint64_t siphash24_finalize(struct siphash *state) { uint64_t b; - b = state->padding | (( ( uint64_t )state->inlen ) << 56); + assert(state); + + b = state->padding | (((uint64_t) state->inlen) << 56); + #ifdef DEBUG - printf("(%3zu) v0 %08x %08x\n", state->inlen, (uint32_t) (state->v0 >> 32), (uint32_t)state->v0); - printf("(%3zu) v1 %08x %08x\n", state->inlen, (uint32_t) (state->v1 >> 32), (uint32_t)state->v1); - printf("(%3zu) v2 %08x %08x\n", state->inlen, (uint32_t) (state->v2 >> 32), (uint32_t)state->v2); - printf("(%3zu) v3 %08x %08x\n", state->inlen, (uint32_t) (state->v3 >> 32), (uint32_t)state->v3); + printf("(%3zu) v0 %08x %08x\n", state->inlen, (uint32_t) (state->v0 >> 32), (uint32_t) state->v0); + printf("(%3zu) v1 %08x %08x\n", state->inlen, (uint32_t) (state->v1 >> 32), (uint32_t) state->v1); + printf("(%3zu) v2 %08x %08x\n", state->inlen, (uint32_t) (state->v2 >> 32), (uint32_t) state->v2); + printf("(%3zu) v3 %08x %08x\n", state->inlen, (uint32_t) (state->v3 >> 32), (uint32_t) state->v3); printf("(%3zu) padding %08x %08x\n", state->inlen, (uint32_t) (state->padding >> 32), (uint32_t) state->padding); #endif + state->v3 ^= b; sipround(state); sipround(state); @@ -169,14 +176,17 @@ void siphash24_finalize(uint8_t out[8], struct siphash *state) { sipround(state); sipround(state); - *(le64_t*)out = htole64(state->v0 ^ state->v1 ^ state->v2 ^ state->v3); + return state->v0 ^ state->v1 ^ state->v2 ^ state->v3; } -/* SipHash-2-4 */ -void siphash24(uint8_t out[8], const void *_in, size_t inlen, const uint8_t k[16]) { +uint64_t siphash24(const void *in, size_t inlen, const uint8_t k[16]) { struct siphash state; + assert(in); + assert(k); + siphash24_init(&state, k); - siphash24_compress(_in, inlen, &state); - siphash24_finalize(out, &state); + siphash24_compress(in, inlen, &state); + + return siphash24_finalize(&state); } diff --git a/src/basic/siphash24.h b/src/basic/siphash24.h index 6c5cd98ee8..ba4f7d01b6 100644 --- a/src/basic/siphash24.h +++ b/src/basic/siphash24.h @@ -4,16 +4,16 @@ #include <sys/types.h> struct siphash { - uint64_t v0; - uint64_t v1; - uint64_t v2; - uint64_t v3; - uint64_t padding; - size_t inlen; + uint64_t v0; + uint64_t v1; + uint64_t v2; + uint64_t v3; + uint64_t padding; + size_t inlen; }; void siphash24_init(struct siphash *state, const uint8_t k[16]); void siphash24_compress(const void *in, size_t inlen, struct siphash *state); -void siphash24_finalize(uint8_t out[8], struct siphash *state); +uint64_t siphash24_finalize(struct siphash *state); -void siphash24(uint8_t out[8], const void *in, size_t inlen, const uint8_t k[16]); +uint64_t siphash24(const void *in, size_t inlen, const uint8_t k[16]); diff --git a/src/basic/socket-util.h b/src/basic/socket-util.h index c60f2556af..129ffa811c 100644 --- a/src/basic/socket-util.h +++ b/src/basic/socket-util.h @@ -21,9 +21,9 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <sys/socket.h> -#include <netinet/in.h> #include <netinet/ether.h> +#include <netinet/in.h> +#include <sys/socket.h> #include <sys/un.h> #include <linux/netlink.h> #include <linux/if_packet.h> diff --git a/src/basic/stat-util.h b/src/basic/stat-util.h index 909b220a24..fb92464274 100644 --- a/src/basic/stat-util.h +++ b/src/basic/stat-util.h @@ -52,9 +52,8 @@ int path_is_os_tree(const char *path); int files_same(const char *filea, const char *fileb); /* The .f_type field of struct statfs is really weird defined on - * different archs. Let's use our own type we know is sufficiently - * larger to store the possible values. */ -typedef long statfs_f_type_t; + * different archs. Let's give its type a name. */ +typedef typeof(((struct statfs*)NULL)->f_type) statfs_f_type_t; bool is_fs_type(const struct statfs *s, statfs_f_type_t magic_value) _pure_; int fd_check_fstype(int fd, statfs_f_type_t magic_value); diff --git a/src/basic/strv.c b/src/basic/strv.c index ba6df716a7..771781f9fc 100644 --- a/src/basic/strv.c +++ b/src/basic/strv.c @@ -27,8 +27,8 @@ #include "alloc-util.h" #include "escape.h" #include "string-util.h" -#include "util.h" #include "strv.h" +#include "util.h" char *strv_find(char **l, const char *name) { char **i; diff --git a/src/basic/strxcpyx.c b/src/basic/strxcpyx.c index 6542c0abf5..088ba53c29 100644 --- a/src/basic/strxcpyx.c +++ b/src/basic/strxcpyx.c @@ -27,6 +27,7 @@ #include <stdio.h> #include <string.h> + #include "strxcpyx.h" size_t strpcpy(char **dest, size_t size, const char *src) { diff --git a/src/basic/terminal-util.h b/src/basic/terminal-util.h index f2185c1c11..b2c7a297ae 100644 --- a/src/basic/terminal-util.h +++ b/src/basic/terminal-util.h @@ -19,8 +19,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <stdbool.h> #include <stdarg.h> +#include <stdbool.h> #include <stdio.h> #include "macro.h" diff --git a/src/basic/time-util.c b/src/basic/time-util.c index b36fbe4f09..b9da6991da 100644 --- a/src/basic/time-util.c +++ b/src/basic/time-util.c @@ -27,6 +27,7 @@ #include "fd-util.h" #include "fileio.h" #include "fs-util.h" +#include "parse-util.h" #include "path-util.h" #include "string-util.h" #include "strv.h" @@ -323,15 +324,15 @@ char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) { const char *suffix; usec_t usec; } table[] = { - { "y", USEC_PER_YEAR }, - { "month", USEC_PER_MONTH }, - { "w", USEC_PER_WEEK }, - { "d", USEC_PER_DAY }, - { "h", USEC_PER_HOUR }, - { "min", USEC_PER_MINUTE }, - { "s", USEC_PER_SEC }, - { "ms", USEC_PER_MSEC }, - { "us", 1 }, + { "y", USEC_PER_YEAR }, + { "month", USEC_PER_MONTH }, + { "w", USEC_PER_WEEK }, + { "d", USEC_PER_DAY }, + { "h", USEC_PER_HOUR }, + { "min", USEC_PER_MINUTE }, + { "s", USEC_PER_SEC }, + { "ms", USEC_PER_MSEC }, + { "us", 1 }, }; unsigned i; @@ -658,29 +659,18 @@ int parse_timestamp(const char *t, usec_t *usec) { parse_usec: { - char *end; - unsigned long long val; - size_t l; + unsigned add; k++; - if (*k < '0' || *k > '9') + r = parse_fractional_part_u(&k, 6, &add); + if (r < 0) return -EINVAL; - /* base 10 instead of base 0, .09 is not base 8 */ - errno = 0; - val = strtoull(k, &end, 10); - if (*end || errno) + if (*k) return -EINVAL; - l = end-k; - - /* val has l digits, make them 6 */ - for (; l < 6; l++) - val *= 10; - for (; l > 6; l--) - val /= 10; + x_usec = add; - x_usec = val; } from_tm: @@ -711,33 +701,34 @@ int parse_time(const char *t, usec_t *usec, usec_t default_unit) { const char *suffix; usec_t usec; } table[] = { - { "seconds", USEC_PER_SEC }, - { "second", USEC_PER_SEC }, - { "sec", USEC_PER_SEC }, - { "s", USEC_PER_SEC }, + { "seconds", USEC_PER_SEC }, + { "second", USEC_PER_SEC }, + { "sec", USEC_PER_SEC }, + { "s", USEC_PER_SEC }, { "minutes", USEC_PER_MINUTE }, - { "minute", USEC_PER_MINUTE }, - { "min", USEC_PER_MINUTE }, - { "months", USEC_PER_MONTH }, - { "month", USEC_PER_MONTH }, - { "msec", USEC_PER_MSEC }, - { "ms", USEC_PER_MSEC }, - { "m", USEC_PER_MINUTE }, - { "hours", USEC_PER_HOUR }, - { "hour", USEC_PER_HOUR }, - { "hr", USEC_PER_HOUR }, - { "h", USEC_PER_HOUR }, - { "days", USEC_PER_DAY }, - { "day", USEC_PER_DAY }, - { "d", USEC_PER_DAY }, - { "weeks", USEC_PER_WEEK }, - { "week", USEC_PER_WEEK }, - { "w", USEC_PER_WEEK }, - { "years", USEC_PER_YEAR }, - { "year", USEC_PER_YEAR }, - { "y", USEC_PER_YEAR }, - { "usec", 1ULL }, - { "us", 1ULL }, + { "minute", USEC_PER_MINUTE }, + { "min", USEC_PER_MINUTE }, + { "months", USEC_PER_MONTH }, + { "month", USEC_PER_MONTH }, + { "M", USEC_PER_MONTH }, + { "msec", USEC_PER_MSEC }, + { "ms", USEC_PER_MSEC }, + { "m", USEC_PER_MINUTE }, + { "hours", USEC_PER_HOUR }, + { "hour", USEC_PER_HOUR }, + { "hr", USEC_PER_HOUR }, + { "h", USEC_PER_HOUR }, + { "days", USEC_PER_DAY }, + { "day", USEC_PER_DAY }, + { "d", USEC_PER_DAY }, + { "weeks", USEC_PER_WEEK }, + { "week", USEC_PER_WEEK }, + { "w", USEC_PER_WEEK }, + { "years", USEC_PER_YEAR }, + { "year", USEC_PER_YEAR }, + { "y", USEC_PER_YEAR }, + { "usec", 1ULL }, + { "us", 1ULL }, }; const char *p, *s; diff --git a/src/basic/unaligned.h b/src/basic/unaligned.h index d6181dd9a9..a8115eaa1f 100644 --- a/src/basic/unaligned.h +++ b/src/basic/unaligned.h @@ -21,8 +21,11 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <endian.h> #include <stdint.h> +/* BE */ + static inline uint16_t unaligned_read_be16(const void *_u) { const uint8_t *u = _u; @@ -64,3 +67,47 @@ static inline void unaligned_write_be64(void *_u, uint64_t a) { unaligned_write_be32(u, (uint32_t) (a >> 32)); unaligned_write_be32(u + 4, (uint32_t) a); } + +/* LE */ + +static inline uint16_t unaligned_read_le16(const void *_u) { + const uint8_t *u = _u; + + return (((uint16_t) u[1]) << 8) | + ((uint16_t) u[0]); +} + +static inline uint32_t unaligned_read_le32(const void *_u) { + const uint8_t *u = _u; + + return (((uint32_t) unaligned_read_le16(u + 2)) << 16) | + ((uint32_t) unaligned_read_le16(u)); +} + +static inline uint64_t unaligned_read_le64(const void *_u) { + const uint8_t *u = _u; + + return (((uint64_t) unaligned_read_le32(u + 4)) << 32) | + ((uint64_t) unaligned_read_le32(u)); +} + +static inline void unaligned_write_le16(void *_u, uint16_t a) { + uint8_t *u = _u; + + u[0] = (uint8_t) a; + u[1] = (uint8_t) (a >> 8); +} + +static inline void unaligned_write_le32(void *_u, uint32_t a) { + uint8_t *u = _u; + + unaligned_write_le16(u, (uint16_t) a); + unaligned_write_le16(u + 2, (uint16_t) (a >> 16)); +} + +static inline void unaligned_write_le64(void *_u, uint64_t a) { + uint8_t *u = _u; + + unaligned_write_le32(u, (uint32_t) a); + unaligned_write_le32(u + 4, (uint32_t) (a >> 32)); +} diff --git a/src/basic/unit-name.c b/src/basic/unit-name.c index 710421508c..9a55eacbfb 100644 --- a/src/basic/unit-name.c +++ b/src/basic/unit-name.c @@ -597,7 +597,6 @@ const char* unit_dbus_interface_from_type(UnitType t) { [UNIT_SOCKET] = "org.freedesktop.systemd1.Socket", [UNIT_BUSNAME] = "org.freedesktop.systemd1.BusName", [UNIT_TARGET] = "org.freedesktop.systemd1.Target", - [UNIT_SNAPSHOT] = "org.freedesktop.systemd1.Snapshot", [UNIT_DEVICE] = "org.freedesktop.systemd1.Device", [UNIT_MOUNT] = "org.freedesktop.systemd1.Mount", [UNIT_AUTOMOUNT] = "org.freedesktop.systemd1.Automount", @@ -819,7 +818,6 @@ static const char* const unit_type_table[_UNIT_TYPE_MAX] = { [UNIT_SOCKET] = "socket", [UNIT_BUSNAME] = "busname", [UNIT_TARGET] = "target", - [UNIT_SNAPSHOT] = "snapshot", [UNIT_DEVICE] = "device", [UNIT_MOUNT] = "mount", [UNIT_AUTOMOUNT] = "automount", @@ -950,13 +948,6 @@ static const char* const slice_state_table[_SLICE_STATE_MAX] = { DEFINE_STRING_TABLE_LOOKUP(slice_state, SliceState); -static const char* const snapshot_state_table[_SNAPSHOT_STATE_MAX] = { - [SNAPSHOT_DEAD] = "dead", - [SNAPSHOT_ACTIVE] = "active" -}; - -DEFINE_STRING_TABLE_LOOKUP(snapshot_state, SnapshotState); - static const char* const socket_state_table[_SOCKET_STATE_MAX] = { [SOCKET_DEAD] = "dead", [SOCKET_START_PRE] = "start-pre", @@ -1009,16 +1000,12 @@ DEFINE_STRING_TABLE_LOOKUP(timer_state, TimerState); static const char* const unit_dependency_table[_UNIT_DEPENDENCY_MAX] = { [UNIT_REQUIRES] = "Requires", - [UNIT_REQUIRES_OVERRIDABLE] = "RequiresOverridable", [UNIT_REQUISITE] = "Requisite", - [UNIT_REQUISITE_OVERRIDABLE] = "RequisiteOverridable", [UNIT_WANTS] = "Wants", [UNIT_BINDS_TO] = "BindsTo", [UNIT_PART_OF] = "PartOf", [UNIT_REQUIRED_BY] = "RequiredBy", - [UNIT_REQUIRED_BY_OVERRIDABLE] = "RequiredByOverridable", [UNIT_REQUISITE_OF] = "RequisiteOf", - [UNIT_REQUISITE_OF_OVERRIDABLE] = "RequisiteOfOverridable", [UNIT_WANTED_BY] = "WantedBy", [UNIT_BOUND_BY] = "BoundBy", [UNIT_CONSISTS_OF] = "ConsistsOf", diff --git a/src/basic/unit-name.h b/src/basic/unit-name.h index 65b55d9554..03c1a6e4ac 100644 --- a/src/basic/unit-name.h +++ b/src/basic/unit-name.h @@ -32,7 +32,6 @@ typedef enum UnitType { UNIT_SOCKET, UNIT_BUSNAME, UNIT_TARGET, - UNIT_SNAPSHOT, UNIT_DEVICE, UNIT_MOUNT, UNIT_AUTOMOUNT, @@ -165,13 +164,6 @@ typedef enum SliceState { _SLICE_STATE_INVALID = -1 } SliceState; -typedef enum SnapshotState { - SNAPSHOT_DEAD, - SNAPSHOT_ACTIVE, - _SNAPSHOT_STATE_MAX, - _SNAPSHOT_STATE_INVALID = -1 -} SnapshotState; - typedef enum SocketState { SOCKET_DEAD, SOCKET_START_PRE, @@ -226,18 +218,14 @@ typedef enum TimerState { typedef enum UnitDependency { /* Positive dependencies */ UNIT_REQUIRES, - UNIT_REQUIRES_OVERRIDABLE, UNIT_REQUISITE, - UNIT_REQUISITE_OVERRIDABLE, UNIT_WANTS, UNIT_BINDS_TO, UNIT_PART_OF, /* Inverse of the above */ UNIT_REQUIRED_BY, /* inverse of 'requires' is 'required_by' */ - UNIT_REQUIRED_BY_OVERRIDABLE, /* inverse of 'requires_overridable' is 'required_by_overridable' */ UNIT_REQUISITE_OF, /* inverse of 'requisite' is 'requisite_of' */ - UNIT_REQUISITE_OF_OVERRIDABLE,/* inverse of 'requisite_overridable' is 'requisite_of_overridable' */ UNIT_WANTED_BY, /* inverse of 'wants' */ UNIT_BOUND_BY, /* inverse of 'binds_to' */ UNIT_CONSISTS_OF, /* inverse of 'part_of' */ @@ -366,9 +354,6 @@ ServiceState service_state_from_string(const char *s) _pure_; const char* slice_state_to_string(SliceState i) _const_; SliceState slice_state_from_string(const char *s) _pure_; -const char* snapshot_state_to_string(SnapshotState i) _const_; -SnapshotState snapshot_state_from_string(const char *s) _pure_; - const char* socket_state_to_string(SocketState i) _const_; SocketState socket_state_from_string(const char *s) _pure_; diff --git a/src/basic/user-util.c b/src/basic/user-util.c index d6c936db37..397880b0b1 100644 --- a/src/basic/user-util.c +++ b/src/basic/user-util.c @@ -19,8 +19,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <pwd.h> #include <grp.h> +#include <pwd.h> #include "alloc-util.h" #include "fd-util.h" diff --git a/src/basic/user-util.h b/src/basic/user-util.h index 11ff6674cf..6106e138be 100644 --- a/src/basic/user-util.h +++ b/src/basic/user-util.h @@ -21,8 +21,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <sys/types.h> #include <stdbool.h> +#include <sys/types.h> bool uid_is_valid(uid_t uid); diff --git a/src/basic/utf8.c b/src/basic/utf8.c index 7600d99903..b4063a4cec 100644 --- a/src/basic/utf8.c +++ b/src/basic/utf8.c @@ -44,10 +44,10 @@ */ #include <errno.h> -#include <stdlib.h> #include <inttypes.h> -#include <string.h> #include <stdbool.h> +#include <stdlib.h> +#include <string.h> #include "alloc-util.h" #include "hexdecoct.h" diff --git a/src/basic/util.c b/src/basic/util.c index 08bdcd28f2..58617b354a 100644 --- a/src/basic/util.c +++ b/src/basic/util.c @@ -73,6 +73,7 @@ #include "build.h" #include "def.h" #include "device-nodes.h" +#include "dirent-util.h" #include "env-util.h" #include "escape.h" #include "exit-status.h" @@ -81,19 +82,20 @@ #include "formats-util.h" #include "gunicode.h" #include "hashmap.h" +#include "hexdecoct.h" #include "hostname-util.h" #include "ioprio.h" #include "log.h" #include "macro.h" #include "missing.h" #include "mkdir.h" -#include "hexdecoct.h" #include "parse-util.h" #include "path-util.h" #include "process-util.h" #include "random-util.h" #include "signal-util.h" #include "sparse-endian.h" +#include "stat-util.h" #include "string-table.h" #include "string-util.h" #include "strv.h" @@ -102,8 +104,6 @@ #include "utf8.h" #include "util.h" #include "virt.h" -#include "dirent-util.h" -#include "stat-util.h" /* Put this test here for a lack of better place */ assert_cc(EAGAIN == EWOULDBLOCK); @@ -206,7 +206,7 @@ static int do_execute(char **directories, usec_t timeout, char *argv[]) { log_debug("Spawned %s as " PID_FMT ".", path, pid); - r = hashmap_put(pids, UINT_TO_PTR(pid), path); + r = hashmap_put(pids, PID_TO_PTR(pid), path); if (r < 0) return log_oom(); path = NULL; @@ -224,10 +224,10 @@ static int do_execute(char **directories, usec_t timeout, char *argv[]) { _cleanup_free_ char *path = NULL; pid_t pid; - pid = PTR_TO_UINT(hashmap_first_key(pids)); + pid = PTR_TO_PID(hashmap_first_key(pids)); assert(pid > 0); - path = hashmap_remove(pids, UINT_TO_PTR(pid)); + path = hashmap_remove(pids, PID_TO_PTR(pid)); assert(path); wait_for_terminate_and_warn(path, pid, true); diff --git a/src/basic/virt.c b/src/basic/virt.c index d088b7a804..b82680a54b 100644 --- a/src/basic/virt.c +++ b/src/basic/virt.c @@ -24,6 +24,8 @@ #include <unistd.h> #include "alloc-util.h" +#include "dirent-util.h" +#include "fd-util.h" #include "fileio.h" #include "process-util.h" #include "stat-util.h" @@ -267,13 +269,20 @@ int detect_vm(void) { if (cached_found >= 0) return cached_found; - r = detect_vm_cpuid(); + /* We have to use the correct order here: + * Some virtualization technologies do use KVM hypervisor but are + * expected to be detected as something else. So detect DMI first. + * + * An example is Virtualbox since version 5.0, which uses KVM backend. + * Detection via DMI works corretly, the CPU ID would find KVM + * only. */ + r = detect_vm_dmi(); if (r < 0) return r; if (r != VIRTUALIZATION_NONE) goto finish; - r = detect_vm_dmi(); + r = detect_vm_cpuid(); if (r < 0) return r; if (r != VIRTUALIZATION_NONE) diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c index 38b79da886..6d35adc0e2 100644 --- a/src/boot/efi/boot.c +++ b/src/boot/efi/boot.c @@ -18,12 +18,12 @@ #include <efi.h> #include <efilib.h> -#include "util.h" #include "console.h" -#include "graphics.h" -#include "pefile.h" #include "disk.h" +#include "graphics.h" #include "linux.h" +#include "pefile.h" +#include "util.h" #ifndef EFI_OS_INDICATIONS_BOOT_TO_FW_UI #define EFI_OS_INDICATIONS_BOOT_TO_FW_UI 0x0000000000000001ULL diff --git a/src/boot/efi/console.c b/src/boot/efi/console.c index 66aa88f32e..2151d34432 100644 --- a/src/boot/efi/console.c +++ b/src/boot/efi/console.c @@ -18,8 +18,8 @@ #include <efi.h> #include <efilib.h> -#include "util.h" #include "console.h" +#include "util.h" #define EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID \ { 0xdd9e7534, 0x7762, 0x4698, { 0x8c, 0x14, 0xf5, 0x85, 0x17, 0xa6, 0x25, 0xaa } } diff --git a/src/boot/efi/graphics.c b/src/boot/efi/graphics.c index f732428216..efa91fa7ac 100644 --- a/src/boot/efi/graphics.c +++ b/src/boot/efi/graphics.c @@ -20,8 +20,8 @@ #include <efi.h> #include <efilib.h> -#include "util.h" #include "graphics.h" +#include "util.h" EFI_STATUS graphics_mode(BOOLEAN on) { #define EFI_CONSOLE_CONTROL_PROTOCOL_GUID \ diff --git a/src/boot/efi/linux.c b/src/boot/efi/linux.c index 809c69310e..e9d097c132 100644 --- a/src/boot/efi/linux.c +++ b/src/boot/efi/linux.c @@ -17,8 +17,8 @@ #include <efi.h> #include <efilib.h> -#include "util.h" #include "linux.h" +#include "util.h" #define SETUP_MAGIC 0x53726448 /* "HdrS" */ struct SetupHeader { diff --git a/src/boot/efi/pefile.c b/src/boot/efi/pefile.c index e6fedbc929..efb3271ee3 100644 --- a/src/boot/efi/pefile.c +++ b/src/boot/efi/pefile.c @@ -17,8 +17,8 @@ #include <efi.h> #include <efilib.h> -#include "util.h" #include "pefile.h" +#include "util.h" struct DosFileHeader { UINT8 Magic[2]; diff --git a/src/boot/efi/splash.c b/src/boot/efi/splash.c index 470ea3e2cc..b584b5e6a9 100644 --- a/src/boot/efi/splash.c +++ b/src/boot/efi/splash.c @@ -18,9 +18,9 @@ #include <efi.h> #include <efilib.h> -#include "util.h" #include "graphics.h" #include "splash.h" +#include "util.h" struct bmp_file { CHAR8 signature[2]; diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c index 0c5ee4e9ff..2cd5c33cb6 100644 --- a/src/boot/efi/stub.c +++ b/src/boot/efi/stub.c @@ -16,12 +16,12 @@ #include <efi.h> #include <efilib.h> -#include "util.h" -#include "pefile.h" #include "disk.h" #include "graphics.h" -#include "splash.h" #include "linux.h" +#include "pefile.h" +#include "splash.h" +#include "util.h" /* magic string to find in the binary image */ static const char __attribute__((used)) magic[] = "#### LoaderInfo: systemd-stub " VERSION " ####"; diff --git a/src/bootchart/bootchart.h b/src/bootchart/bootchart.h index bdb4b00199..8432a2a119 100644 --- a/src/bootchart/bootchart.h +++ b/src/bootchart/bootchart.h @@ -25,6 +25,7 @@ ***/ #include <stdbool.h> + #include "list.h" #define MAXCPUS 16 diff --git a/src/bootchart/store.h b/src/bootchart/store.h index bbb4796efd..4d2e0d439f 100644 --- a/src/bootchart/store.h +++ b/src/bootchart/store.h @@ -25,6 +25,7 @@ ***/ #include <dirent.h> + #include "bootchart.h" double gettime_ns(void); diff --git a/src/bootchart/svg.c b/src/bootchart/svg.c index 05330c0577..2bf473ffc1 100644 --- a/src/bootchart/svg.c +++ b/src/bootchart/svg.c @@ -22,25 +22,25 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <fcntl.h> +#include <limits.h> #include <stdio.h> #include <string.h> +#include <sys/utsname.h> #include <time.h> -#include <limits.h> #include <unistd.h> -#include <sys/utsname.h> -#include <fcntl.h> #include "alloc-util.h" #include "architecture.h" -#include "util.h" +#include "bootchart.h" +#include "fd-util.h" #include "fileio.h" +#include "list.h" #include "macro.h" #include "store.h" #include "svg.h" -#include "bootchart.h" -#include "list.h" #include "utf8.h" -#include "fd-util.h" +#include "util.h" #define time_to_graph(t) ((t) * arg_scale_x) #define ps_to_graph(n) ((n) * arg_scale_y) diff --git a/src/bus-proxyd/bus-xml-policy.c b/src/bus-proxyd/bus-xml-policy.c index f0834e9525..debd58ce8b 100644 --- a/src/bus-proxyd/bus-xml-policy.c +++ b/src/bus-proxyd/bus-xml-policy.c @@ -392,11 +392,11 @@ static int file_load(Policy *p, const char *path) { } else { PolicyItem *first; - first = hashmap_get(p->user_items, UINT32_TO_PTR(i->uid)); + first = hashmap_get(p->user_items, UID_TO_PTR(i->uid)); item_append(i, &first); i->uid_valid = true; - r = hashmap_replace(p->user_items, UINT32_TO_PTR(i->uid), first); + r = hashmap_replace(p->user_items, UID_TO_PTR(i->uid), first); if (r < 0) { LIST_REMOVE(items, first, i); return log_oom(); @@ -424,11 +424,11 @@ static int file_load(Policy *p, const char *path) { } else { PolicyItem *first; - first = hashmap_get(p->group_items, UINT32_TO_PTR(i->gid)); + first = hashmap_get(p->group_items, GID_TO_PTR(i->gid)); item_append(i, &first); i->gid_valid = true; - r = hashmap_replace(p->group_items, UINT32_TO_PTR(i->gid), first); + r = hashmap_replace(p->group_items, GID_TO_PTR(i->gid), first); if (r < 0) { LIST_REMOVE(items, first, i); return log_oom(); @@ -787,7 +787,7 @@ static int policy_check(Policy *p, const struct policy_check_filter *filter) { verdict = check_policy_items(p->default_items, filter); if (filter->gid != GID_INVALID) { - items = hashmap_get(p->group_items, UINT32_TO_PTR(filter->gid)); + items = hashmap_get(p->group_items, GID_TO_PTR(filter->gid)); if (items) { v = check_policy_items(items, filter); if (v != DUNNO) @@ -796,7 +796,7 @@ static int policy_check(Policy *p, const struct policy_check_filter *filter) { } if (filter->uid != UID_INVALID) { - items = hashmap_get(p->user_items, UINT32_TO_PTR(filter->uid)); + items = hashmap_get(p->user_items, UID_TO_PTR(filter->uid)); if (items) { v = check_policy_items(items, filter); if (v != DUNNO) @@ -1155,7 +1155,7 @@ static void dump_hashmap_items(Hashmap *h) { void *k; HASHMAP_FOREACH_KEY(i, k, h, j) { - printf("\t%s Item for %u:\n", draw_special_char(DRAW_ARROW), PTR_TO_UINT(k)); + printf("\t%s Item for " UID_FMT ":\n", draw_special_char(DRAW_ARROW), PTR_TO_UID(k)); dump_items(i, "\t\t"); } } diff --git a/src/bus-proxyd/bus-xml-policy.h b/src/bus-proxyd/bus-xml-policy.h index 8f0ab8f17f..8dde0cb868 100644 --- a/src/bus-proxyd/bus-xml-policy.h +++ b/src/bus-proxyd/bus-xml-policy.h @@ -23,8 +23,8 @@ #include <pthread.h> -#include "list.h" #include "hashmap.h" +#include "list.h" typedef enum PolicyItemType { _POLICY_ITEM_TYPE_UNSET = 0, diff --git a/src/bus-proxyd/driver.h b/src/bus-proxyd/driver.h index da3834f8b0..9f68902441 100644 --- a/src/bus-proxyd/driver.h +++ b/src/bus-proxyd/driver.h @@ -22,6 +22,7 @@ ***/ #include "sd-bus.h" + #include "bus-xml-policy.h" #include "proxy.h" diff --git a/src/bus-proxyd/stdio-bridge.c b/src/bus-proxyd/stdio-bridge.c index 6e47884209..2dc5fe631b 100644 --- a/src/bus-proxyd/stdio-bridge.c +++ b/src/bus-proxyd/stdio-bridge.c @@ -27,8 +27,8 @@ #include <string.h> #include <unistd.h> -#include "sd-daemon.h" #include "sd-bus.h" +#include "sd-daemon.h" #include "alloc-util.h" #include "bus-internal.h" diff --git a/src/core/audit-fd.c b/src/core/audit-fd.c index 3ae46d8cfb..0a484d89fc 100644 --- a/src/core/audit-fd.c +++ b/src/core/audit-fd.c @@ -21,16 +21,17 @@ #include <errno.h> + #include "audit-fd.h" #ifdef HAVE_AUDIT -#include <stdbool.h> #include <libaudit.h> +#include <stdbool.h> +#include "fd-util.h" #include "log.h" #include "util.h" -#include "fd-util.h" static bool initialized = false; static int audit_fd; diff --git a/src/core/automount.c b/src/core/automount.c index 4c229247c5..85b7b4e842 100644 --- a/src/core/automount.c +++ b/src/core/automount.c @@ -148,6 +148,9 @@ static int automount_add_default_dependencies(Automount *a) { assert(a); + if (!UNIT(a)->default_dependencies) + return 0; + if (UNIT(a)->manager->running_as != MANAGER_SYSTEM) return 0; @@ -219,11 +222,9 @@ static int automount_load(Unit *u) { if (r < 0) return r; - if (UNIT(a)->default_dependencies) { - r = automount_add_default_dependencies(a); - if (r < 0) - return r; - } + r = automount_add_default_dependencies(a); + if (r < 0) + return r; } return automount_verify(a); @@ -727,8 +728,7 @@ static void automount_enter_runnning(Automount *a) { if (!S_ISDIR(st.st_mode) || st.st_dev != a->dev_id) log_unit_info(UNIT(a), "Automount point already active?"); else { - r = manager_add_job(UNIT(a)->manager, JOB_START, UNIT_TRIGGER(UNIT(a)), - JOB_REPLACE, true, &error, NULL); + r = manager_add_job(UNIT(a)->manager, JOB_START, UNIT_TRIGGER(UNIT(a)), JOB_REPLACE, &error, NULL); if (r < 0) { log_unit_warning(UNIT(a), "Failed to queue mount startup job: %s", bus_error_message(&error, r)); goto fail; @@ -973,7 +973,7 @@ static int automount_dispatch_io(sd_event_source *s, int fd, uint32_t events, vo break; } - r = manager_add_job(UNIT(a)->manager, JOB_STOP, UNIT_TRIGGER(UNIT(a)), JOB_REPLACE, true, &error, NULL); + r = manager_add_job(UNIT(a)->manager, JOB_STOP, UNIT_TRIGGER(UNIT(a)), JOB_REPLACE, &error, NULL); if (r < 0) { log_unit_warning(UNIT(a), "Failed to queue umount startup job: %s", bus_error_message(&error, r)); goto fail; diff --git a/src/core/bus-endpoint.h b/src/core/bus-endpoint.h index 4a31f4c4be..f6c5f7c5af 100644 --- a/src/core/bus-endpoint.h +++ b/src/core/bus-endpoint.h @@ -24,8 +24,8 @@ typedef struct BusEndpoint BusEndpoint; typedef struct BusEndpointPolicy BusEndpointPolicy; -#include "hashmap.h" #include "bus-policy.h" +#include "hashmap.h" struct BusEndpointPolicy { char *name; diff --git a/src/core/bus-policy.h b/src/core/bus-policy.h index 3b04f5457a..2f61289185 100644 --- a/src/core/bus-policy.h +++ b/src/core/bus-policy.h @@ -21,9 +21,9 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include "kdbus.h" #include "list.h" #include "macro.h" -#include "kdbus.h" typedef struct BusNamePolicy BusNamePolicy; diff --git a/src/core/busname.c b/src/core/busname.c index 68508e20d2..04fa12a4da 100644 --- a/src/core/busname.c +++ b/src/core/busname.c @@ -596,7 +596,7 @@ static void busname_enter_running(BusName *n) { goto fail; } - r = manager_add_job(UNIT(n)->manager, JOB_START, UNIT_DEREF(n->service), JOB_REPLACE, true, &error, NULL); + r = manager_add_job(UNIT(n)->manager, JOB_START, UNIT_DEREF(n->service), JOB_REPLACE, &error, NULL); if (r < 0) goto fail; } diff --git a/src/core/cgroup.c b/src/core/cgroup.c index bed01fde21..d122175417 100644 --- a/src/core/cgroup.c +++ b/src/core/cgroup.c @@ -217,7 +217,7 @@ static int whitelist_device(const char *path, const char *node, const char *acc) r = cg_set_attribute("devices", path, "devices.allow", buf); if (r < 0) - log_full_errno(IN_SET(r, -ENOENT, -EROFS, -EINVAL) ? LOG_DEBUG : LOG_WARNING, r, + log_full_errno(IN_SET(r, -ENOENT, -EROFS, -EINVAL, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, "Failed to set devices.allow on %s: %m", path); return r; @@ -288,7 +288,7 @@ static int whitelist_major(const char *path, const char *name, char type, const r = cg_set_attribute("devices", path, "devices.allow", buf); if (r < 0) - log_full_errno(IN_SET(r, -ENOENT, -EROFS, -EINVAL) ? LOG_DEBUG : LOG_WARNING, r, + log_full_errno(IN_SET(r, -ENOENT, -EROFS, -EINVAL, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, "Failed to set devices.allow on %s: %m", path); } @@ -328,13 +328,13 @@ void cgroup_context_apply(CGroupContext *c, CGroupMask mask, const char *path, u c->cpu_shares != CGROUP_CPU_SHARES_INVALID ? c->cpu_shares : CGROUP_CPU_SHARES_DEFAULT); r = cg_set_attribute("cpu", path, "cpu.shares", buf); if (r < 0) - log_full_errno(IN_SET(r, -ENOENT, -EROFS) ? LOG_DEBUG : LOG_WARNING, r, + log_full_errno(IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, "Failed to set cpu.shares on %s: %m", path); sprintf(buf, USEC_FMT "\n", CGROUP_CPU_QUOTA_PERIOD_USEC); r = cg_set_attribute("cpu", path, "cpu.cfs_period_us", buf); if (r < 0) - log_full_errno(IN_SET(r, -ENOENT, -EROFS) ? LOG_DEBUG : LOG_WARNING, r, + log_full_errno(IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, "Failed to set cpu.cfs_period_us on %s: %m", path); if (c->cpu_quota_per_sec_usec != USEC_INFINITY) { @@ -343,7 +343,7 @@ void cgroup_context_apply(CGroupContext *c, CGroupMask mask, const char *path, u } else r = cg_set_attribute("cpu", path, "cpu.cfs_quota_us", "-1"); if (r < 0) - log_full_errno(IN_SET(r, -ENOENT, -EROFS) ? LOG_DEBUG : LOG_WARNING, r, + log_full_errno(IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, "Failed to set cpu.cfs_quota_us on %s: %m", path); } @@ -359,7 +359,7 @@ void cgroup_context_apply(CGroupContext *c, CGroupMask mask, const char *path, u c->blockio_weight != CGROUP_BLKIO_WEIGHT_INVALID ? c->blockio_weight : CGROUP_BLKIO_WEIGHT_DEFAULT); r = cg_set_attribute("blkio", path, "blkio.weight", buf); if (r < 0) - log_full_errno(IN_SET(r, -ENOENT, -EROFS) ? LOG_DEBUG : LOG_WARNING, r, + log_full_errno(IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, "Failed to set blkio.weight on %s: %m", path); /* FIXME: no way to reset this list */ @@ -373,7 +373,7 @@ void cgroup_context_apply(CGroupContext *c, CGroupMask mask, const char *path, u sprintf(buf, "%u:%u %" PRIu64 "\n", major(dev), minor(dev), w->weight); r = cg_set_attribute("blkio", path, "blkio.weight_device", buf); if (r < 0) - log_full_errno(IN_SET(r, -ENOENT, -EROFS) ? LOG_DEBUG : LOG_WARNING, r, + log_full_errno(IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, "Failed to set blkio.weight_device on %s: %m", path); } } @@ -392,7 +392,7 @@ void cgroup_context_apply(CGroupContext *c, CGroupMask mask, const char *path, u sprintf(buf, "%u:%u %" PRIu64 "\n", major(dev), minor(dev), b->bandwidth); r = cg_set_attribute("blkio", path, a, buf); if (r < 0) - log_full_errno(IN_SET(r, -ENOENT, -EROFS) ? LOG_DEBUG : LOG_WARNING, r, + log_full_errno(IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, "Failed to set %s on %s: %m", a, path); } } @@ -416,7 +416,7 @@ void cgroup_context_apply(CGroupContext *c, CGroupMask mask, const char *path, u } if (r < 0) - log_full_errno(IN_SET(r, -ENOENT, -EROFS) ? LOG_DEBUG : LOG_WARNING, r, + log_full_errno(IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, "Failed to set memory.limit_in_bytes/memory.max on %s: %m", path); } @@ -432,7 +432,7 @@ void cgroup_context_apply(CGroupContext *c, CGroupMask mask, const char *path, u else r = cg_set_attribute("devices", path, "devices.allow", "a"); if (r < 0) - log_full_errno(IN_SET(r, -ENOENT, -EROFS, -EINVAL) ? LOG_DEBUG : LOG_WARNING, r, + log_full_errno(IN_SET(r, -ENOENT, -EROFS, -EINVAL, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, "Failed to reset devices.list on %s: %m", path); if (c->device_policy == CGROUP_CLOSED || @@ -494,7 +494,7 @@ void cgroup_context_apply(CGroupContext *c, CGroupMask mask, const char *path, u r = cg_set_attribute("pids", path, "pids.max", "max"); if (r < 0) - log_full_errno(IN_SET(r, -ENOENT, -EROFS) ? LOG_DEBUG : LOG_WARNING, r, + log_full_errno(IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, "Failed to set pids.max on %s: %m", path); } @@ -505,7 +505,7 @@ void cgroup_context_apply(CGroupContext *c, CGroupMask mask, const char *path, u r = cg_set_attribute("net_cls", path, "net_cls.classid", buf); if (r < 0) - log_full_errno(IN_SET(r, -ENOENT, -EROFS) ? LOG_DEBUG : LOG_WARNING, r, + log_full_errno(IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, "Failed to set net_cls.classid on %s: %m", path); } } diff --git a/src/core/cgroup.h b/src/core/cgroup.h index 457544b49f..1b18d06652 100644 --- a/src/core/cgroup.h +++ b/src/core/cgroup.h @@ -112,8 +112,8 @@ struct CGroupContext { bool delegate; }; -#include "unit.h" #include "cgroup-util.h" +#include "unit.h" void cgroup_context_init(CGroupContext *c); void cgroup_context_done(CGroupContext *c); diff --git a/src/core/dbus-automount.c b/src/core/dbus-automount.c index 45f2c2ffd6..54830a515b 100644 --- a/src/core/dbus-automount.c +++ b/src/core/dbus-automount.c @@ -21,8 +21,8 @@ #include "automount.h" #include "bus-util.h" -#include "string-util.h" #include "dbus-automount.h" +#include "string-util.h" static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_result, automount_result, AutomountResult); diff --git a/src/core/dbus-busname.c b/src/core/dbus-busname.c index 05ac89c3c0..445b237643 100644 --- a/src/core/dbus-busname.c +++ b/src/core/dbus-busname.c @@ -21,9 +21,9 @@ #include "bus-util.h" #include "busname.h" +#include "dbus-busname.h" #include "string-util.h" #include "unit.h" -#include "dbus-busname.h" static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_result, busname_result, BusNameResult); diff --git a/src/core/dbus-device.c b/src/core/dbus-device.c index cb156fd37c..97e4a47556 100644 --- a/src/core/dbus-device.c +++ b/src/core/dbus-device.c @@ -19,9 +19,9 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include "unit.h" -#include "device.h" #include "dbus-device.h" +#include "device.h" +#include "unit.h" const sd_bus_vtable bus_device_vtable[] = { SD_BUS_VTABLE_START(0), diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c index db4206a523..093179c003 100644 --- a/src/core/dbus-execute.c +++ b/src/core/dbus-execute.c @@ -629,6 +629,7 @@ const sd_bus_vtable bus_exec_vtable[] = { SD_BUS_VTABLE_START(0), SD_BUS_PROPERTY("Environment", "as", NULL, offsetof(ExecContext, environment), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("EnvironmentFiles", "a(sb)", property_get_environment_files, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("PassEnvironment", "as", NULL, offsetof(ExecContext, pass_environment), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("UMask", "u", bus_property_get_mode, offsetof(ExecContext, umask), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("LimitCPU", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_CPU]), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("LimitFSIZE", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_FSIZE]), SD_BUS_VTABLE_PROPERTY_CONST), @@ -1289,6 +1290,38 @@ int bus_exec_context_set_transient_property( return 1; + } else if (streq(name, "PassEnvironment")) { + + _cleanup_strv_free_ char **l = NULL; + + r = sd_bus_message_read_strv(message, &l); + if (r < 0) + return r; + + if (!strv_env_name_is_valid(l)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid PassEnvironment block."); + + if (mode != UNIT_CHECK) { + if (strv_isempty(l)) { + c->pass_environment = strv_free(c->pass_environment); + unit_write_drop_in_private_format(u, mode, name, "PassEnvironment=\n"); + } else { + _cleanup_free_ char *joined = NULL; + + r = strv_extend_strv(&c->pass_environment, l, true); + if (r < 0) + return r; + + joined = strv_join_quoted(c->pass_environment); + if (!joined) + return -ENOMEM; + + unit_write_drop_in_private_format(u, mode, name, "PassEnvironment=%s\n", joined); + } + } + + return 1; + } else if (STR_IN_SET(name, "ReadWriteDirectories", "ReadOnlyDirectories", "InaccessibleDirectories")) { _cleanup_strv_free_ char **l = NULL; diff --git a/src/core/dbus-kill.c b/src/core/dbus-kill.c index 3b8116281c..c633eb1b76 100644 --- a/src/core/dbus-kill.c +++ b/src/core/dbus-kill.c @@ -19,11 +19,10 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include "signal-util.h" #include "bus-util.h" - -#include "kill.h" #include "dbus-kill.h" +#include "kill.h" +#include "signal-util.h" static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_kill_mode, kill_mode, KillMode); diff --git a/src/core/dbus-kill.h b/src/core/dbus-kill.h index 794c402048..1d32fca547 100644 --- a/src/core/dbus-kill.h +++ b/src/core/dbus-kill.h @@ -23,8 +23,8 @@ #include "sd-bus.h" -#include "unit.h" #include "kill.h" +#include "unit.h" extern const sd_bus_vtable bus_kill_vtable[]; diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c index 1f9f25093d..4d730290b2 100644 --- a/src/core/dbus-manager.c +++ b/src/core/dbus-manager.c @@ -31,7 +31,6 @@ #include "dbus-execute.h" #include "dbus-job.h" #include "dbus-manager.h" -#include "dbus-snapshot.h" #include "dbus-unit.h" #include "dbus.h" #include "env-util.h" @@ -645,6 +644,7 @@ static int transient_unit_from_message( Unit **unit, sd_bus_error *error) { + UnitType t; Unit *u; int r; @@ -652,12 +652,18 @@ static int transient_unit_from_message( assert(message); assert(name); + t = unit_name_to_type(name); + if (t < 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid unit name or type."); + + if (!unit_vtable[t]->can_transient) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unit type %s does not support transient units.", unit_type_to_string(t)); + r = manager_load_unit(m, name, NULL, error, &u); if (r < 0) return r; - if (u->load_state != UNIT_NOT_FOUND || - set_size(u->dependencies[UNIT_REFERENCED_BY]) > 0) + if (!unit_is_pristine(u)) return sd_bus_error_setf(error, BUS_ERROR_UNIT_EXISTS, "Unit %s already exists.", name); /* OK, the unit failed to load and is unreferenced, now let's @@ -671,6 +677,9 @@ static int transient_unit_from_message( if (r < 0) return r; + /* Now load the missing bits of the unit we just created */ + manager_dispatch_load_queue(m); + *unit = u; return 0; @@ -681,8 +690,6 @@ static int transient_aux_units_from_message( sd_bus_message *message, sd_bus_error *error) { - Unit *u; - char *name = NULL; int r; assert(m); @@ -693,20 +700,17 @@ static int transient_aux_units_from_message( return r; while ((r = sd_bus_message_enter_container(message, 'r', "sa(sv)")) > 0) { + const char *name = NULL; + Unit *u; + r = sd_bus_message_read(message, "s", &name); if (r < 0) return r; r = transient_unit_from_message(m, message, name, &u, error); - if (r < 0 && r != -EEXIST) + if (r < 0) return r; - if (r != -EEXIST) { - r = unit_load(u); - if (r < 0) - return r; - } - r = sd_bus_message_exit_container(message); if (r < 0) return r; @@ -725,7 +729,6 @@ static int method_start_transient_unit(sd_bus_message *message, void *userdata, const char *name, *smode; Manager *m = userdata; JobMode mode; - UnitType t; Unit *u; int r; @@ -740,13 +743,6 @@ static int method_start_transient_unit(sd_bus_message *message, void *userdata, if (r < 0) return r; - t = unit_name_to_type(name); - if (t < 0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid unit type."); - - if (!unit_vtable[t]->can_transient) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unit type %s does not support transient units.", unit_type_to_string(t)); - mode = job_mode_from_string(smode); if (mode < 0) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Job mode %s is invalid.", smode); @@ -765,13 +761,6 @@ static int method_start_transient_unit(sd_bus_message *message, void *userdata, if (r < 0) return r; - /* And load this stub fully */ - r = unit_load(u); - if (r < 0) - return r; - - manager_dispatch_load_queue(m); - /* Finally, start it */ return bus_unit_queue_job(message, u, JOB_START, mode, false, error); } @@ -1101,66 +1090,8 @@ static int method_dump(sd_bus_message *message, void *userdata, sd_bus_error *er return sd_bus_reply_method_return(message, "s", dump); } -static int method_create_snapshot(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_free_ char *path = NULL; - Manager *m = userdata; - const char *name; - int cleanup; - Snapshot *s = NULL; - int r; - - assert(message); - assert(m); - - r = mac_selinux_access_check(message, "start", error); - if (r < 0) - return r; - - r = sd_bus_message_read(message, "sb", &name, &cleanup); - if (r < 0) - return r; - - if (isempty(name)) - name = NULL; - - r = bus_verify_manage_units_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 = snapshot_create(m, name, cleanup, error, &s); - if (r < 0) - return r; - - path = unit_dbus_path(UNIT(s)); - if (!path) - return -ENOMEM; - - return sd_bus_reply_method_return(message, "o", path); -} - -static int method_remove_snapshot(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Manager *m = userdata; - const char *name; - Unit *u; - int r; - - assert(message); - assert(m); - - r = sd_bus_message_read(message, "s", &name); - if (r < 0) - return r; - - u = manager_get_unit(m, name); - if (!u) - return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s does not exist.", name); - - if (u->type != UNIT_SNAPSHOT) - return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not a snapshot", name); - - return bus_snapshot_method_remove(message, u, error); +static int method_refuse_snapshot(sd_bus_message *message, void *userdata, sd_bus_error *error) { + return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Support for snapshots has been removed."); } static int method_reload(sd_bus_message *message, void *userdata, sd_bus_error *error) { @@ -1580,9 +1511,9 @@ static int method_get_unit_file_state(sd_bus_message *message, void *userdata, s scope = m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER; - state = unit_file_get_state(scope, NULL, name); - if (state < 0) - return state; + r = unit_file_get_state(scope, NULL, name, &state); + if (r < 0) + return r; return sd_bus_reply_method_return(message, "s", unit_file_state_to_string(state)); } @@ -1710,6 +1641,8 @@ static int method_enable_unit_files_generic( 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; @@ -1944,6 +1877,8 @@ static int method_add_dependency_unit_files(sd_bus_message *message, void *userd 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; @@ -2015,6 +1950,7 @@ const sd_bus_vtable bus_manager_vtable[] = { SD_BUS_PROPERTY("DefaultLimitNICE", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_NICE]), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("DefaultLimitRTPRIO", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_RTPRIO]), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("DefaultLimitRTTIME", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_RTTIME]), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("DefaultTasksMax", "t", NULL, offsetof(Manager, default_tasks_max), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("TimerSlackNSec", "t", property_get_timer_slack_nsec, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_METHOD("GetUnit", "s", "o", method_get_unit, SD_BUS_VTABLE_UNPRIVILEGED), @@ -2042,8 +1978,8 @@ const sd_bus_vtable bus_manager_vtable[] = { SD_BUS_METHOD("Subscribe", NULL, NULL, method_subscribe, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("Unsubscribe", NULL, NULL, method_unsubscribe, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("Dump", NULL, "s", method_dump, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("CreateSnapshot", "sb", "o", method_create_snapshot, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("RemoveSnapshot", "s", NULL, method_remove_snapshot, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("CreateSnapshot", "sb", "o", method_refuse_snapshot, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("RemoveSnapshot", "s", NULL, method_refuse_snapshot, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("Reload", NULL, NULL, method_reload, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("Reexecute", NULL, NULL, method_reexecute, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("Exit", NULL, NULL, method_exit, 0), diff --git a/src/core/dbus-mount.c b/src/core/dbus-mount.c index 0c91850c52..bc5751a10d 100644 --- a/src/core/dbus-mount.c +++ b/src/core/dbus-mount.c @@ -23,10 +23,10 @@ #include "dbus-cgroup.h" #include "dbus-execute.h" #include "dbus-kill.h" +#include "dbus-mount.h" #include "mount.h" #include "string-util.h" #include "unit.h" -#include "dbus-mount.h" static int property_get_what( sd_bus *bus, @@ -118,7 +118,6 @@ const sd_bus_vtable bus_mount_vtable[] = { SD_BUS_PROPERTY("ControlPID", "u", bus_property_get_pid, offsetof(Mount, control_pid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("DirectoryMode", "u", bus_property_get_mode, offsetof(Mount, directory_mode), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("SloppyOptions", "b", bus_property_get_bool, offsetof(Mount, sloppy_options), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("SmackFileSystemRootLabel", "s", NULL, offsetof(Mount, smack_fs_root_label), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("Result", "s", property_get_result, offsetof(Mount, result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), BUS_EXEC_COMMAND_VTABLE("ExecMount", offsetof(Mount, exec_command[MOUNT_EXEC_MOUNT]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), BUS_EXEC_COMMAND_VTABLE("ExecUnmount", offsetof(Mount, exec_command[MOUNT_EXEC_UNMOUNT]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), diff --git a/src/core/dbus-path.c b/src/core/dbus-path.c index 9e32b5fb06..e0544e9161 100644 --- a/src/core/dbus-path.c +++ b/src/core/dbus-path.c @@ -20,10 +20,10 @@ ***/ #include "bus-util.h" +#include "dbus-path.h" #include "path.h" #include "string-util.h" #include "unit.h" -#include "dbus-path.h" static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_result, path_result, PathResult); diff --git a/src/core/dbus-scope.h b/src/core/dbus-scope.h index 33beda47b7..4fb0b25e09 100644 --- a/src/core/dbus-scope.h +++ b/src/core/dbus-scope.h @@ -22,6 +22,7 @@ ***/ #include "sd-bus.h" + #include "unit.h" extern const sd_bus_vtable bus_scope_vtable[]; diff --git a/src/core/dbus-service.h b/src/core/dbus-service.h index aab9f7aa26..a67b64ab5b 100644 --- a/src/core/dbus-service.h +++ b/src/core/dbus-service.h @@ -22,6 +22,7 @@ ***/ #include "sd-bus.h" + #include "unit.h" extern const sd_bus_vtable bus_service_vtable[]; diff --git a/src/core/dbus-slice.c b/src/core/dbus-slice.c index 09e78d1f33..469e3e1c93 100644 --- a/src/core/dbus-slice.c +++ b/src/core/dbus-slice.c @@ -19,10 +19,10 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include "unit.h" -#include "slice.h" #include "dbus-cgroup.h" #include "dbus-slice.h" +#include "slice.h" +#include "unit.h" const sd_bus_vtable bus_slice_vtable[] = { SD_BUS_VTABLE_START(0), diff --git a/src/core/dbus-slice.h b/src/core/dbus-slice.h index eadc3b1a9c..117d11471b 100644 --- a/src/core/dbus-slice.h +++ b/src/core/dbus-slice.h @@ -22,6 +22,7 @@ ***/ #include "sd-bus.h" + #include "unit.h" extern const sd_bus_vtable bus_slice_vtable[]; diff --git a/src/core/dbus-snapshot.c b/src/core/dbus-snapshot.c deleted file mode 100644 index cfe44c9c15..0000000000 --- a/src/core/dbus-snapshot.c +++ /dev/null @@ -1,55 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -/*** - This file is part of systemd. - - Copyright 2010 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 "selinux-access.h" -#include "unit.h" -#include "dbus.h" -#include "snapshot.h" -#include "dbus-snapshot.h" - -int bus_snapshot_method_remove(sd_bus_message *message, void *userdata, sd_bus_error *error) { - Snapshot *s = userdata; - int r; - - assert(message); - assert(s); - - r = mac_selinux_unit_access_check(UNIT(s), message, "stop", error); - if (r < 0) - return r; - - r = bus_verify_manage_units_async(UNIT(s)->manager, 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 */ - - snapshot_remove(s); - - return sd_bus_reply_method_return(message, NULL); -} - -const sd_bus_vtable bus_snapshot_vtable[] = { - SD_BUS_VTABLE_START(0), - SD_BUS_PROPERTY("Cleanup", "b", bus_property_get_bool, offsetof(Snapshot, cleanup), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_METHOD("Remove", NULL, NULL, bus_snapshot_method_remove, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_VTABLE_END -}; diff --git a/src/core/dbus-socket.c b/src/core/dbus-socket.c index be5ef261a6..895dd07753 100644 --- a/src/core/dbus-socket.c +++ b/src/core/dbus-socket.c @@ -150,6 +150,7 @@ const sd_bus_vtable bus_socket_vtable[] = { SD_BUS_PROPERTY("NConnections", "u", bus_property_get_unsigned, offsetof(Socket, n_connections), 0), SD_BUS_PROPERTY("NAccepted", "u", bus_property_get_unsigned, offsetof(Socket, n_accepted), 0), SD_BUS_PROPERTY("FileDescriptorName", "s", property_get_fdname, 0, 0), + SD_BUS_PROPERTY("SocketProtocol", "i", bus_property_get_int, offsetof(Socket, socket_protocol), SD_BUS_VTABLE_PROPERTY_CONST), BUS_EXEC_COMMAND_LIST_VTABLE("ExecStartPre", offsetof(Socket, exec_command[SOCKET_EXEC_START_PRE]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), BUS_EXEC_COMMAND_LIST_VTABLE("ExecStartPost", offsetof(Socket, exec_command[SOCKET_EXEC_START_POST]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), BUS_EXEC_COMMAND_LIST_VTABLE("ExecStopPre", offsetof(Socket, exec_command[SOCKET_EXEC_STOP_PRE]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), diff --git a/src/core/dbus-socket.h b/src/core/dbus-socket.h index 17164d9871..8dad6ea2e9 100644 --- a/src/core/dbus-socket.h +++ b/src/core/dbus-socket.h @@ -22,6 +22,7 @@ ***/ #include "sd-bus.h" + #include "unit.h" extern const sd_bus_vtable bus_socket_vtable[]; diff --git a/src/core/dbus-swap.c b/src/core/dbus-swap.c index 603ca95fd9..f2a0f1d172 100644 --- a/src/core/dbus-swap.c +++ b/src/core/dbus-swap.c @@ -23,10 +23,10 @@ #include "bus-util.h" #include "dbus-cgroup.h" #include "dbus-execute.h" +#include "dbus-swap.h" #include "string-util.h" #include "swap.h" #include "unit.h" -#include "dbus-swap.h" static int property_get_priority( sd_bus *bus, diff --git a/src/core/dbus-swap.h b/src/core/dbus-swap.h index 9469f68ab8..a414ca7f75 100644 --- a/src/core/dbus-swap.h +++ b/src/core/dbus-swap.h @@ -23,6 +23,7 @@ ***/ #include "sd-bus.h" + #include "unit.h" extern const sd_bus_vtable bus_swap_vtable[]; diff --git a/src/core/dbus-target.c b/src/core/dbus-target.c index 350f5c3ed2..654bcf1a29 100644 --- a/src/core/dbus-target.c +++ b/src/core/dbus-target.c @@ -19,8 +19,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include "unit.h" #include "dbus-target.h" +#include "unit.h" const sd_bus_vtable bus_target_vtable[] = { SD_BUS_VTABLE_START(0), diff --git a/src/core/dbus-timer.c b/src/core/dbus-timer.c index a8a280d961..ec301df6d7 100644 --- a/src/core/dbus-timer.c +++ b/src/core/dbus-timer.c @@ -180,8 +180,10 @@ const sd_bus_vtable bus_timer_vtable[] = { BUS_PROPERTY_DUAL_TIMESTAMP("LastTriggerUSec", offsetof(Timer, last_trigger), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("Result", "s", property_get_result, offsetof(Timer, result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("AccuracyUSec", "t", bus_property_get_usec, offsetof(Timer, accuracy_usec), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("RandomizedDelayUSec", "t", bus_property_get_usec, offsetof(Timer, random_usec), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("Persistent", "b", bus_property_get_bool, offsetof(Timer, persistent), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("WakeSystem", "b", bus_property_get_bool, offsetof(Timer, wake_system), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("RemainAfterElapse", "b", bus_property_get_bool, offsetof(Timer, remain_after_elapse), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_VTABLE_END }; @@ -282,8 +284,23 @@ static int bus_timer_set_transient_property( return 1; - } else if (streq(name, "WakeSystem")) { + } else if (streq(name, "RandomizedDelayUSec")) { + usec_t u = 0; + + r = sd_bus_message_read(message, "t", &u); + if (r < 0) + return r; + + if (mode != UNIT_CHECK) { + char time[FORMAT_TIMESPAN_MAX]; + + t->random_usec = u; + unit_write_drop_in_private_format(UNIT(t), mode, name, "RandomizedDelaySec=%s\n", format_timespan(time, sizeof(time), u, USEC_PER_MSEC)); + } + + return 1; + } else if (streq(name, "WakeSystem")) { int b; r = sd_bus_message_read(message, "b", &b); @@ -292,11 +309,24 @@ static int bus_timer_set_transient_property( if (mode != UNIT_CHECK) { t->wake_system = b; - unit_write_drop_in_private_format(UNIT(t), mode, name, "%s=%s\n", name, yes_no(t->wake_system)); + unit_write_drop_in_private_format(UNIT(t), mode, name, "%s=%s\n", name, yes_no(b)); } return 1; + } else if (streq(name, "RemainAfterElapse")) { + int b; + + r = sd_bus_message_read(message, "b", &b); + if (r < 0) + return r; + + if (mode != UNIT_CHECK) { + t->remain_after_elapse = b; + unit_write_drop_in_private_format(UNIT(t), mode, name, "%s=%s\n", name, yes_no(b)); + } + + return 1; } return 0; diff --git a/src/core/dbus-timer.h b/src/core/dbus-timer.h index 103172f055..ca35c4b8c1 100644 --- a/src/core/dbus-timer.h +++ b/src/core/dbus-timer.h @@ -22,6 +22,7 @@ ***/ #include "sd-bus.h" + #include "unit.h" extern const sd_bus_vtable bus_timer_vtable[]; diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c index 6320cd1aa9..d9b7382c82 100644 --- a/src/core/dbus-unit.c +++ b/src/core/dbus-unit.c @@ -118,6 +118,22 @@ static int property_get_dependencies( return sd_bus_message_close_container(reply); } +static int property_get_obsolete_dependencies( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + assert(bus); + assert(reply); + + /* For dependency types we don't support anymore always return an empty array */ + return sd_bus_message_append(reply, "as", 0); +} + static int property_get_description( sd_bus *bus, const char *path, @@ -621,16 +637,12 @@ const sd_bus_vtable bus_unit_vtable[] = { SD_BUS_PROPERTY("Names", "as", property_get_names, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("Following", "s", property_get_following, 0, 0), SD_BUS_PROPERTY("Requires", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_REQUIRES]), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("RequiresOverridable", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_REQUIRES_OVERRIDABLE]), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("Requisite", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_REQUISITE]), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("RequisiteOverridable", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_REQUISITE_OVERRIDABLE]), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("Wants", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_WANTS]), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("BindsTo", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_BINDS_TO]), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("PartOf", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_PART_OF]), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("RequiredBy", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_REQUIRED_BY]), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("RequiredByOverridable", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_REQUIRED_BY_OVERRIDABLE]), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("RequisiteOf", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_REQUISITE_OF]), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("RequisiteOfOverridable", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_REQUISITE_OF_OVERRIDABLE]), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("WantedBy", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_WANTED_BY]), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("BoundBy", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_BOUND_BY]), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("ConsistsOf", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_CONSISTS_OF]), SD_BUS_VTABLE_PROPERTY_CONST), @@ -644,6 +656,10 @@ const sd_bus_vtable bus_unit_vtable[] = { SD_BUS_PROPERTY("PropagatesReloadTo", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_PROPAGATES_RELOAD_TO]), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("ReloadPropagatedFrom", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_RELOAD_PROPAGATED_FROM]), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("JoinsNamespaceOf", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_JOINS_NAMESPACE_OF]), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("RequiresOverridable", "as", property_get_obsolete_dependencies, 0, SD_BUS_VTABLE_HIDDEN), + SD_BUS_PROPERTY("RequisiteOverridable", "as", property_get_obsolete_dependencies, 0, SD_BUS_VTABLE_HIDDEN), + SD_BUS_PROPERTY("RequiredByOverridable", "as", property_get_obsolete_dependencies, 0, SD_BUS_VTABLE_HIDDEN), + SD_BUS_PROPERTY("RequisiteOfOverridable", "as", property_get_obsolete_dependencies, 0, SD_BUS_VTABLE_HIDDEN), SD_BUS_PROPERTY("RequiresMountsFor", "as", NULL, offsetof(Unit, requires_mounts_for), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("Documentation", "as", NULL, offsetof(Unit, documentation), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("Description", "s", property_get_description, 0, SD_BUS_VTABLE_PROPERTY_CONST), @@ -671,7 +687,6 @@ const sd_bus_vtable bus_unit_vtable[] = { SD_BUS_PROPERTY("DefaultDependencies", "b", bus_property_get_bool, offsetof(Unit, default_dependencies), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("OnFailureJobMode", "s", property_get_job_mode, offsetof(Unit, on_failure_job_mode), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("IgnoreOnIsolate", "b", bus_property_get_bool, offsetof(Unit, ignore_on_isolate), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("IgnoreOnSnapshot", "b", bus_property_get_bool, offsetof(Unit, ignore_on_snapshot), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("NeedDaemonReload", "b", property_get_need_daemon_reload, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("JobTimeoutUSec", "t", bus_property_get_usec, offsetof(Unit, job_timeout), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("JobTimeoutAction", "s", property_get_failure_action, offsetof(Unit, job_timeout_action), SD_BUS_VTABLE_PROPERTY_CONST), @@ -993,7 +1008,7 @@ int bus_unit_queue_job( (type == JOB_RELOAD_OR_START && job_type_collapse(type, u) == JOB_START && u->refuse_manual_start)) return sd_bus_error_setf(error, BUS_ERROR_ONLY_BY_DEPENDENCY, "Operation refused, unit %s may be requested by dependency only.", u->id); - r = manager_add_job(u->manager, type, u, mode, true, error, &j); + r = manager_add_job(u->manager, type, u, mode, error, &j); if (r < 0) return r; @@ -1109,9 +1124,15 @@ static int bus_unit_set_transient_property( UnitDependency d; const char *other; - d = unit_dependency_from_string(name); - if (d < 0) - return -EINVAL; + if (streq(name, "RequiresOverridable")) + d = UNIT_REQUIRES; /* redirect for obsolete unit dependency type */ + else if (streq(name, "RequisiteOverridable")) + d = UNIT_REQUISITE; /* same here */ + else { + d = unit_dependency_from_string(name); + if (d < 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid unit dependency: %s", name); + } r = sd_bus_message_enter_container(message, 'a', "s"); if (r < 0) diff --git a/src/core/dbus-unit.h b/src/core/dbus-unit.h index b622e0ae8d..b8c6ec398a 100644 --- a/src/core/dbus-unit.h +++ b/src/core/dbus-unit.h @@ -22,6 +22,7 @@ ***/ #include "sd-bus.h" + #include "unit.h" extern const sd_bus_vtable bus_unit_vtable[]; diff --git a/src/core/dbus.c b/src/core/dbus.c index 6c44b28adf..7932130036 100644 --- a/src/core/dbus.c +++ b/src/core/dbus.c @@ -177,7 +177,7 @@ static int signal_activation_request(sd_bus_message *message, void *userdata, sd goto failed; } - r = manager_add_job(m, JOB_START, u, JOB_REPLACE, true, &error, NULL); + r = manager_add_job(m, JOB_START, u, JOB_REPLACE, &error, NULL); if (r < 0) goto failed; diff --git a/src/core/device.c b/src/core/device.c index 23ee7aee7e..bcd4d1146b 100644 --- a/src/core/device.c +++ b/src/core/device.c @@ -117,7 +117,6 @@ static void device_init(Unit *u) { u->job_timeout = u->manager->default_timeout_start_usec; u->ignore_on_isolate = true; - u->ignore_on_snapshot = true; } static void device_done(Unit *u) { @@ -602,7 +601,7 @@ static void device_shutdown(Manager *m) { m->devices_by_sysfs = hashmap_free(m->devices_by_sysfs); } -static int device_enumerate(Manager *m) { +static void device_enumerate(Manager *m) { _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL; struct udev_list_entry *item = NULL, *first = NULL; int r; @@ -612,7 +611,7 @@ static int device_enumerate(Manager *m) { if (!m->udev_monitor) { m->udev_monitor = udev_monitor_new_from_netlink(m->udev, "udev"); if (!m->udev_monitor) { - r = -ENOMEM; + log_oom(); goto fail; } @@ -622,37 +621,49 @@ static int device_enumerate(Manager *m) { (void) udev_monitor_set_receive_buffer_size(m->udev_monitor, 128*1024*1024); r = udev_monitor_filter_add_match_tag(m->udev_monitor, "systemd"); - if (r < 0) + if (r < 0) { + log_error_errno(r, "Failed to add udev tag match: %m"); goto fail; + } r = udev_monitor_enable_receiving(m->udev_monitor); - if (r < 0) + if (r < 0) { + log_error_errno(r, "Failed to enable udev event reception: %m"); goto fail; + } r = sd_event_add_io(m->event, &m->udev_event_source, udev_monitor_get_fd(m->udev_monitor), EPOLLIN, device_dispatch_io, m); - if (r < 0) + if (r < 0) { + log_error_errno(r, "Failed to watch udev file descriptor: %m"); goto fail; + } (void) sd_event_source_set_description(m->udev_event_source, "device"); } e = udev_enumerate_new(m->udev); if (!e) { - r = -ENOMEM; + log_oom(); goto fail; } r = udev_enumerate_add_match_tag(e, "systemd"); - if (r < 0) + if (r < 0) { + log_error_errno(r, "Failed to create udev tag enumeration: %m"); goto fail; + } r = udev_enumerate_add_match_is_initialized(e); - if (r < 0) + if (r < 0) { + log_error_errno(r, "Failed to install initialization match into enumeration: %m"); goto fail; + } r = udev_enumerate_scan_devices(e); - if (r < 0) + if (r < 0) { + log_error_errno(r, "Failed to enumerate devices: %m"); goto fail; + } first = udev_enumerate_get_list_entry(e); udev_list_entry_foreach(item, first) { @@ -675,13 +686,10 @@ static int device_enumerate(Manager *m) { device_update_found_by_sysfs(m, sysfs, true, DEVICE_FOUND_UDEV, false); } - return 0; + return; fail: - log_error_errno(r, "Failed to enumerate devices: %m"); - device_shutdown(m); - return r; } static int device_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata) { diff --git a/src/core/execute.c b/src/core/execute.c index d751065af0..677480cbe1 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -1332,6 +1332,34 @@ static int build_environment( return 0; } +static int build_pass_environment(const ExecContext *c, char ***ret) { + _cleanup_strv_free_ char **pass_env = NULL; + size_t n_env = 0, n_bufsize = 0; + char **i; + + STRV_FOREACH(i, c->pass_environment) { + _cleanup_free_ char *x = NULL; + char *v; + + v = getenv(*i); + if (!v) + continue; + x = strjoin(*i, "=", v, NULL); + if (!x) + return -ENOMEM; + if (!GREEDY_REALLOC(pass_env, n_bufsize, n_env + 2)) + return -ENOMEM; + pass_env[n_env++] = x; + pass_env[n_env] = NULL; + x = NULL; + } + + *ret = pass_env; + pass_env = NULL; + + return 0; +} + static bool exec_needs_mount_namespace( const ExecContext *context, const ExecParameters *params, @@ -1412,7 +1440,7 @@ static int exec_child( char **files_env, int *exit_status) { - _cleanup_strv_free_ char **our_env = NULL, **pam_env = NULL, **final_env = NULL, **final_argv = NULL; + _cleanup_strv_free_ char **our_env = NULL, **pass_env = NULL, **pam_env = NULL, **final_env = NULL, **final_argv = NULL; _cleanup_free_ char *mac_selinux_context_net = NULL; const char *username = NULL, *home = NULL, *shell = NULL, *wd; uid_t uid = UID_INVALID; @@ -1928,9 +1956,16 @@ static int exec_child( return r; } - final_env = strv_env_merge(5, + r = build_pass_environment(context, &pass_env); + if (r < 0) { + *exit_status = EXIT_MEMORY; + return r; + } + + final_env = strv_env_merge(6, params->environment, our_env, + pass_env, context->environment, files_env, pam_env, @@ -2088,6 +2123,7 @@ void exec_context_done(ExecContext *c) { c->environment = strv_free(c->environment); c->environment_files = strv_free(c->environment_files); + c->pass_environment = strv_free(c->pass_environment); for (l = 0; l < ELEMENTSOF(c->rlimit); l++) c->rlimit[l] = mfree(c->rlimit[l]); @@ -2358,6 +2394,9 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) { STRV_FOREACH(e, c->environment_files) fprintf(f, "%sEnvironmentFile: %s\n", prefix, *e); + STRV_FOREACH(e, c->pass_environment) + fprintf(f, "%sPassEnvironment: %s\n", prefix, *e); + fprintf(f, "%sRuntimeDirectoryMode: %04o\n", prefix, c->runtime_directory_mode); STRV_FOREACH(d, c->runtime_directory) @@ -2375,8 +2414,8 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) { for (i = 0; i < RLIM_NLIMITS; i++) if (c->rlimit[i]) - fprintf(f, "%s%s: "RLIM_FMT"\n", - prefix, rlimit_to_string(i), c->rlimit[i]->rlim_max); + fprintf(f, "%s%s: " RLIM_FMT " " RLIM_FMT "\n", + prefix, rlimit_to_string(i), c->rlimit[i]->rlim_cur, c->rlimit[i]->rlim_max); if (c->ioprio_set) { _cleanup_free_ char *class_str = NULL; diff --git a/src/core/execute.h b/src/core/execute.h index f8995a4203..be5be9f531 100644 --- a/src/core/execute.h +++ b/src/core/execute.h @@ -27,16 +27,16 @@ typedef struct ExecContext ExecContext; typedef struct ExecRuntime ExecRuntime; typedef struct ExecParameters ExecParameters; -#include <sys/capability.h> +#include <sched.h> #include <stdbool.h> #include <stdio.h> -#include <sched.h> +#include <sys/capability.h> -#include "list.h" +#include "bus-endpoint.h" #include "fdset.h" +#include "list.h" #include "missing.h" #include "namespace.h" -#include "bus-endpoint.h" typedef enum ExecUtmpMode { EXEC_UTMP_INIT, @@ -99,6 +99,7 @@ struct ExecRuntime { struct ExecContext { char **environment; char **environment_files; + char **pass_environment; struct rlimit *rlimit[_RLIMIT_MAX]; char *working_directory, *root_directory; @@ -203,8 +204,8 @@ struct ExecContext { BusEndpoint *bus_endpoint; }; -#include "cgroup.h" #include "cgroup-util.h" +#include "cgroup.h" struct ExecParameters { char **argv; diff --git a/src/core/failure-action.c b/src/core/failure-action.c index c7c95984b7..f67fb05af0 100644 --- a/src/core/failure-action.c +++ b/src/core/failure-action.c @@ -42,8 +42,6 @@ int failure_action( FailureAction action, const char *reboot_arg) { - int r; - assert(m); assert(action >= 0); assert(action < _FAILURE_ACTION_MAX); @@ -62,18 +60,13 @@ int failure_action( switch (action) { - case FAILURE_ACTION_REBOOT: { - _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; - + case FAILURE_ACTION_REBOOT: log_and_status(m, "Rebooting as result of failure."); update_reboot_param_file(reboot_arg); - r = manager_add_job_by_name(m, JOB_START, SPECIAL_REBOOT_TARGET, JOB_REPLACE, true, &error, NULL); - if (r < 0) - log_error("Failed to reboot: %s.", bus_error_message(&error, r)); + (void) manager_add_job_by_name_and_warn(m, JOB_START, SPECIAL_REBOOT_TARGET, JOB_REPLACE, NULL); break; - } case FAILURE_ACTION_REBOOT_FORCE: log_and_status(m, "Forcibly rebooting as result of failure."); @@ -96,17 +89,10 @@ int failure_action( reboot(RB_AUTOBOOT); break; - case FAILURE_ACTION_POWEROFF: { - _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; - + case FAILURE_ACTION_POWEROFF: log_and_status(m, "Powering off as result of failure."); - - r = manager_add_job_by_name(m, JOB_START, SPECIAL_POWEROFF_TARGET, JOB_REPLACE, true, &error, NULL); - if (r < 0) - log_error("Failed to poweroff: %s.", bus_error_message(&error, r)); - + (void) manager_add_job_by_name_and_warn(m, JOB_START, SPECIAL_POWEROFF_TARGET, JOB_REPLACE, NULL); break; - } case FAILURE_ACTION_POWEROFF_FORCE: log_and_status(m, "Forcibly powering off as result of failure."); diff --git a/src/core/hostname-setup.c b/src/core/hostname-setup.c index 3645f9c515..d92a9a764f 100644 --- a/src/core/hostname-setup.c +++ b/src/core/hostname-setup.c @@ -25,12 +25,12 @@ #include "alloc-util.h" #include "fileio.h" +#include "hostname-setup.h" #include "hostname-util.h" #include "log.h" #include "macro.h" #include "string-util.h" #include "util.h" -#include "hostname-setup.h" int hostname_setup(void) { int r; diff --git a/src/core/ima-setup.c b/src/core/ima-setup.c index 9572fa17d9..4f42ae6f31 100644 --- a/src/core/ima-setup.c +++ b/src/core/ima-setup.c @@ -21,8 +21,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <unistd.h> #include <errno.h> +#include <unistd.h> #include "fd-util.h" #include "fileio.h" diff --git a/src/core/job.c b/src/core/job.c index 120381fc3b..9654590635 100644 --- a/src/core/job.c +++ b/src/core/job.c @@ -175,7 +175,6 @@ static void job_merge_into_installed(Job *j, Job *other) { else assert(other->type == JOB_NOP); - j->override = j->override || other->override; j->irreversible = j->irreversible || other->irreversible; j->ignore_order = j->ignore_order || other->ignore_order; } @@ -307,12 +306,10 @@ void job_dump(Job *j, FILE*f, const char *prefix) { "%s-> Job %u:\n" "%s\tAction: %s -> %s\n" "%s\tState: %s\n" - "%s\tForced: %s\n" "%s\tIrreversible: %s\n", prefix, j->id, prefix, j->unit->id, job_type_to_string(j->type), prefix, job_state_to_string(j->state), - prefix, yes_no(j->override), prefix, yes_no(j->irreversible)); } @@ -503,17 +500,26 @@ static void job_change_type(Job *j, JobType newtype) { } static int job_perform_on_unit(Job **j) { - /* While we execute this operation the job might go away (for - * example: because it finishes immediately or is replaced by a new, - * conflicting job.) To make sure we don't access a freed job later on - * we store the id here, so that we can verify the job is still - * valid. */ - Manager *m = (*j)->manager; - Unit *u = (*j)->unit; - JobType t = (*j)->type; - uint32_t id = (*j)->id; + uint32_t id; + Manager *m; + JobType t; + Unit *u; int r; + /* While we execute this operation the job might go away (for + * example: because it finishes immediately or is replaced by + * a new, conflicting job.) To make sure we don't access a + * freed job later on we store the id here, so that we can + * verify the job is still valid. */ + + assert(j); + assert(*j); + + m = (*j)->manager; + u = (*j)->unit; + t = (*j)->type; + id = (*j)->id; + switch (t) { case JOB_START: r = unit_start(u); @@ -521,6 +527,7 @@ static int job_perform_on_unit(Job **j) { case JOB_RESTART: t = JOB_STOP; + /* fall through */ case JOB_STOP: r = unit_stop(u); break; @@ -620,8 +627,7 @@ int job_run_and_invalidate(Job *j) { } _pure_ static const char *job_get_status_message_format(Unit *u, JobType t, JobResult result) { - const char *format; - const UnitStatusMessageFormats *format_table; + static const char *const generic_finished_start_job[_JOB_RESULT_MAX] = { [JOB_DONE] = "Started %s.", [JOB_TIMEOUT] = "Timed out starting %s.", @@ -647,11 +653,14 @@ _pure_ static const char *job_get_status_message_format(Unit *u, JobType t, JobR [JOB_SKIPPED] = "%s is not active.", }; + const UnitStatusMessageFormats *format_table; + const char *format; + assert(u); assert(t >= 0); assert(t < _JOB_TYPE_MAX); - if (t == JOB_START || t == JOB_STOP || t == JOB_RESTART) { + if (IN_SET(t, JOB_START, JOB_STOP, JOB_RESTART)) { format_table = &UNIT_VTABLE(u)->status_message_formats; if (format_table) { format = t == JOB_START ? format_table->finished_start_job[result] : @@ -675,7 +684,6 @@ _pure_ static const char *job_get_status_message_format(Unit *u, JobType t, JobR } static void job_print_status_message(Unit *u, JobType t, JobResult result) { - const char *format; static const char* const job_result_status_table[_JOB_RESULT_MAX] = { [JOB_DONE] = ANSI_GREEN " OK " ANSI_NORMAL, [JOB_TIMEOUT] = ANSI_HIGHLIGHT_RED " TIME " ANSI_NORMAL, @@ -686,10 +694,16 @@ static void job_print_status_message(Unit *u, JobType t, JobResult result) { [JOB_UNSUPPORTED] = ANSI_HIGHLIGHT_YELLOW "UNSUPP" ANSI_NORMAL, }; + const char *format; + assert(u); assert(t >= 0); assert(t < _JOB_TYPE_MAX); + /* Reload status messages have traditionally not been printed to console. */ + if (t == JOB_RELOAD) + return; + format = job_get_status_message_format(u, t, result); if (!format) return; @@ -702,10 +716,10 @@ static void job_print_status_message(Unit *u, JobType t, JobResult result) { REENABLE_WARNING; if (t == JOB_START && result == JOB_FAILED) { - _cleanup_free_ char *quoted = shell_maybe_quote(u->id); + _cleanup_free_ char *quoted; - manager_status_printf(u->manager, STATUS_TYPE_NORMAL, NULL, - "See 'systemctl status %s' for details.", strna(quoted)); + quoted = shell_maybe_quote(u->id); + manager_status_printf(u->manager, STATUS_TYPE_NORMAL, NULL, "See 'systemctl status %s' for details.", strna(quoted)); } } @@ -743,13 +757,22 @@ static void job_log_status_message(Unit *u, JobType t, JobResult result) { snprintf(buf, sizeof(buf), format, unit_description(u)); REENABLE_WARNING; - if (t == JOB_START) + switch (t) { + + case JOB_START: mid = result == JOB_DONE ? SD_MESSAGE_UNIT_STARTED : SD_MESSAGE_UNIT_FAILED; - else if (t == JOB_STOP || t == JOB_RESTART) - mid = SD_MESSAGE_UNIT_STOPPED; - else if (t == JOB_RELOAD) + break; + + case JOB_RELOAD: mid = SD_MESSAGE_UNIT_RELOADED; - else { + break; + + case JOB_STOP: + case JOB_RESTART: + mid = SD_MESSAGE_UNIT_STOPPED; + break; + + default: log_struct(job_result_log_level[result], LOG_UNIT_ID(u), LOG_MESSAGE("%s", buf), @@ -773,10 +796,7 @@ static void job_emit_status_message(Unit *u, JobType t, JobResult result) { return; job_log_status_message(u, t, result); - - /* Reload status messages have traditionally not been printed to console. */ - if (t != JOB_RELOAD) - job_print_status_message(u, t, result); + job_print_status_message(u, t, result); } static void job_fail_dependencies(Unit *u, UnitDependency d) { @@ -841,8 +861,6 @@ int job_finish_and_invalidate(Job *j, JobResult result, bool recursive) { job_fail_dependencies(u, UNIT_REQUIRED_BY); job_fail_dependencies(u, UNIT_REQUISITE_OF); job_fail_dependencies(u, UNIT_BOUND_BY); - job_fail_dependencies(u, UNIT_REQUIRED_BY_OVERRIDABLE); - job_fail_dependencies(u, UNIT_REQUISITE_OF_OVERRIDABLE); } else if (t == JOB_STOP) job_fail_dependencies(u, UNIT_CONFLICTED_BY); } @@ -967,7 +985,6 @@ int job_serialize(Job *j, FILE *f, FDSet *fds) { fprintf(f, "job-id=%u\n", j->id); fprintf(f, "job-type=%s\n", job_type_to_string(j->type)); fprintf(f, "job-state=%s\n", job_state_to_string(j->state)); - fprintf(f, "job-override=%s\n", yes_no(j->override)); fprintf(f, "job-irreversible=%s\n", yes_no(j->irreversible)); fprintf(f, "job-sent-dbus-new-signal=%s\n", yes_no(j->sent_dbus_new_signal)); fprintf(f, "job-ignore-order=%s\n", yes_no(j->ignore_order)); @@ -1035,15 +1052,6 @@ int job_deserialize(Job *j, FILE *f, FDSet *fds) { else job_set_state(j, s); - } else if (streq(l, "job-override")) { - int b; - - b = parse_boolean(v); - if (b < 0) - log_debug("Failed to parse job override flag %s", v); - else - j->override = j->override || b; - } else if (streq(l, "job-irreversible")) { int b; diff --git a/src/core/job.h b/src/core/job.h index 350e9f385f..118b24e5b7 100644 --- a/src/core/job.h +++ b/src/core/job.h @@ -26,6 +26,7 @@ #include "sd-event.h" #include "list.h" +#include "unit-name.h" typedef struct Job Job; typedef struct JobDependency JobDependency; @@ -162,7 +163,6 @@ struct Job { bool installed:1; bool in_run_queue:1; bool matters_to_anchor:1; - bool override:1; bool in_dbus_queue:1; bool sent_dbus_new_signal:1; bool ignore_order:1; diff --git a/src/core/kmod-setup.c b/src/core/kmod-setup.c index 651f79a1fe..a6ab8cf4b3 100644 --- a/src/core/kmod-setup.c +++ b/src/core/kmod-setup.c @@ -19,17 +19,17 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <unistd.h> #include <string.h> +#include <unistd.h> #ifdef HAVE_KMOD #include <libkmod.h> #endif -#include "macro.h" -#include "capability-util.h" #include "bus-util.h" +#include "capability-util.h" #include "kmod-setup.h" +#include "macro.h" #ifdef HAVE_KMOD static void systemd_kmod_log( diff --git a/src/core/load-dropin.c b/src/core/load-dropin.c index 11566af51b..3fa66f91aa 100644 --- a/src/core/load-dropin.c +++ b/src/core/load-dropin.c @@ -20,13 +20,13 @@ ***/ -#include "unit.h" +#include "conf-parser.h" #include "load-dropin.h" +#include "load-fragment.h" #include "log.h" #include "strv.h" #include "unit-name.h" -#include "conf-parser.h" -#include "load-fragment.h" +#include "unit.h" static int add_dependency_consumer( UnitDependency dependency, diff --git a/src/core/load-dropin.h b/src/core/load-dropin.h index 1e018c4525..93ffcc4a72 100644 --- a/src/core/load-dropin.h +++ b/src/core/load-dropin.h @@ -21,8 +21,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include "unit.h" #include "dropin.h" +#include "unit.h" /* Read service data supplementary drop-in directories */ diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4 index 75388659e3..0408b9a829 100644 --- a/src/core/load-fragment-gperf.gperf.m4 +++ b/src/core/load-fragment-gperf.gperf.m4 @@ -33,6 +33,7 @@ $1.CPUAffinity, config_parse_exec_cpu_affinity, 0, $1.UMask, config_parse_mode, 0, offsetof($1, exec_context.umask) $1.Environment, config_parse_environ, 0, offsetof($1, exec_context.environment) $1.EnvironmentFile, config_parse_unit_env_file, 0, offsetof($1, exec_context.environment_files) +$1.PassEnvironment, config_parse_pass_environ, 0, offsetof($1, exec_context.pass_environment) $1.StandardInput, config_parse_input, 0, offsetof($1, exec_context.std_input) $1.StandardOutput, config_parse_output, 0, offsetof($1, exec_context.std_output) $1.StandardError, config_parse_output, 0, offsetof($1, exec_context.std_error) @@ -125,7 +126,7 @@ $1.BlockIODeviceWeight, config_parse_blockio_device_weight, 0, $1.BlockIOReadBandwidth, config_parse_blockio_bandwidth, 0, offsetof($1, cgroup_context) $1.BlockIOWriteBandwidth, config_parse_blockio_bandwidth, 0, offsetof($1, cgroup_context) $1.TasksAccounting, config_parse_bool, 0, offsetof($1, cgroup_context.tasks_accounting) -$1.TasksMax, config_parse_tasks_max, 0, offsetof($1, cgroup_context) +$1.TasksMax, config_parse_tasks_max, 0, offsetof($1, cgroup_context.tasks_max) $1.Delegate, config_parse_bool, 0, offsetof($1, cgroup_context.delegate) $1.NetClass, config_parse_netclass, 0, offsetof($1, cgroup_context)' )m4_dnl @@ -133,9 +134,7 @@ Unit.Description, config_parse_unit_string_printf, 0, Unit.Documentation, config_parse_documentation, 0, offsetof(Unit, documentation) Unit.SourcePath, config_parse_path, 0, offsetof(Unit, source_path) Unit.Requires, config_parse_unit_deps, UNIT_REQUIRES, 0 -Unit.RequiresOverridable, config_parse_unit_deps, UNIT_REQUIRES_OVERRIDABLE, 0 Unit.Requisite, config_parse_unit_deps, UNIT_REQUISITE, 0 -Unit.RequisiteOverridable, config_parse_unit_deps, UNIT_REQUISITE_OVERRIDABLE, 0 Unit.Wants, config_parse_unit_deps, UNIT_WANTS, 0 Unit.BindsTo, config_parse_unit_deps, UNIT_BINDS_TO, 0 Unit.BindTo, config_parse_unit_deps, UNIT_BINDS_TO, 0 @@ -149,6 +148,8 @@ Unit.ReloadPropagatedFrom, config_parse_unit_deps, UNIT_RELOAD Unit.PropagateReloadFrom, config_parse_unit_deps, UNIT_RELOAD_PROPAGATED_FROM, 0 Unit.PartOf, config_parse_unit_deps, UNIT_PART_OF, 0 Unit.JoinsNamespaceOf, config_parse_unit_deps, UNIT_JOINS_NAMESPACE_OF, 0 +Unit.RequiresOverridable, config_parse_obsolete_unit_deps, UNIT_REQUIRES, 0 +Unit.RequisiteOverridable, config_parse_obsolete_unit_deps, UNIT_REQUISITE, 0 Unit.RequiresMountsFor, config_parse_unit_requires_mounts_for, 0, 0 Unit.StopWhenUnneeded, config_parse_bool, 0, offsetof(Unit, stop_when_unneeded) Unit.RefuseManualStart, config_parse_bool, 0, offsetof(Unit, refuse_manual_start) @@ -158,7 +159,7 @@ Unit.DefaultDependencies, config_parse_bool, 0, Unit.OnFailureJobMode, config_parse_job_mode, 0, offsetof(Unit, on_failure_job_mode) Unit.OnFailureIsolate, config_parse_job_mode_isolate, 0, offsetof(Unit, on_failure_job_mode) Unit.IgnoreOnIsolate, config_parse_bool, 0, offsetof(Unit, ignore_on_isolate) -Unit.IgnoreOnSnapshot, config_parse_bool, 0, offsetof(Unit, ignore_on_snapshot) +Unit.IgnoreOnSnapshot, config_parse_warn_compat, DISABLED_LEGACY, 0 Unit.JobTimeoutSec, config_parse_sec, 0, offsetof(Unit, job_timeout) Unit.JobTimeoutAction, config_parse_failure_action, 0, offsetof(Unit, job_timeout_action) Unit.JobTimeoutRebootArgument, config_parse_string, 0, offsetof(Unit, job_timeout_reboot_arg) @@ -248,6 +249,7 @@ Socket.ListenNetlink, config_parse_socket_listen, SOCKET_SOCK Socket.ListenSpecial, config_parse_socket_listen, SOCKET_SPECIAL, 0 Socket.ListenMessageQueue, config_parse_socket_listen, SOCKET_MQUEUE, 0 Socket.ListenUSBFunction, config_parse_socket_listen, SOCKET_USB_FUNCTION, 0 +Socket.SocketProtocol, config_parse_socket_protocol, 0, 0 Socket.BindIPv6Only, config_parse_socket_bind, 0, 0, Socket.Backlog, config_parse_unsigned, 0, offsetof(Socket, backlog) Socket.BindToDevice, config_parse_socket_bindtodevice, 0, 0 @@ -319,7 +321,6 @@ Mount.Type, config_parse_string, 0, Mount.TimeoutSec, config_parse_sec, 0, offsetof(Mount, timeout_usec) Mount.DirectoryMode, config_parse_mode, 0, offsetof(Mount, directory_mode) Mount.SloppyOptions, config_parse_bool, 0, offsetof(Mount, sloppy_options) -Mount.SmackFileSystemRootLabel, config_parse_string, 0, offsetof(Mount, smack_fs_root_label) EXEC_CONTEXT_CONFIG_ITEMS(Mount)m4_dnl CGROUP_CONTEXT_CONFIG_ITEMS(Mount)m4_dnl KILL_CONTEXT_CONFIG_ITEMS(Mount)m4_dnl @@ -344,7 +345,9 @@ Timer.OnUnitActiveSec, config_parse_timer, 0, Timer.OnUnitInactiveSec, config_parse_timer, 0, 0 Timer.Persistent, config_parse_bool, 0, offsetof(Timer, persistent) Timer.WakeSystem, config_parse_bool, 0, offsetof(Timer, wake_system) +Timer.RemainAfterElapse, config_parse_bool, 0, offsetof(Timer, remain_after_elapse) Timer.AccuracySec, config_parse_sec, 0, offsetof(Timer, accuracy_usec) +Timer.RandomizedDelaySec, config_parse_sec, 0, offsetof(Timer, random_usec) Timer.Unit, config_parse_trigger_unit, 0, 0 m4_dnl Path.PathExists, config_parse_path_spec, 0, 0 diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index 79cabd26e7..3c124495b6 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -32,8 +32,8 @@ #include <sys/resource.h> #include <sys/stat.h> -#include "alloc-util.h" #include "af-list.h" +#include "alloc-util.h" #include "bus-error.h" #include "bus-internal.h" #include "bus-util.h" @@ -98,16 +98,17 @@ int config_parse_warn_compat( return 0; } -int config_parse_unit_deps(const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { +int config_parse_unit_deps( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { UnitDependency d = ltype; Unit *u = userdata; @@ -122,7 +123,7 @@ int config_parse_unit_deps(const char *unit, _cleanup_free_ char *word = NULL, *k = NULL; int r; - r = extract_first_word(&p, &word, NULL, 0); + r = extract_first_word(&p, &word, NULL, EXTRACT_RETAIN_ESCAPE); if (r == 0) break; if (r == -ENOMEM) @@ -146,6 +147,24 @@ int config_parse_unit_deps(const char *unit, return 0; } +int config_parse_obsolete_unit_deps( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + log_syntax(unit, LOG_WARNING, filename, line, 0, + "Unit dependency type %s= is obsolete, replacing by %s=, please update your unit file", lvalue, unit_dependency_to_string(ltype)); + + return config_parse_unit_deps(unit, filename, line, section, section_line, lvalue, ltype, rvalue, data, userdata); +} + int config_parse_unit_string_printf( const char *unit, const char *filename, @@ -402,6 +421,37 @@ int config_parse_socket_listen(const char *unit, return 0; } +int config_parse_socket_protocol(const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + Socket *s; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + s = SOCKET(data); + + if (streq(rvalue, "udplite")) + s->socket_protocol = IPPROTO_UDPLITE; + else if (streq(rvalue, "sctp")) + s->socket_protocol = IPPROTO_SCTP; + else { + log_syntax(unit, LOG_ERR, filename, line, 0, "Socket protocol not supported, ignoring: %s", rvalue); + return 0; + } + + return 0; +} + int config_parse_socket_bind(const char *unit, const char *filename, unsigned line, @@ -1039,59 +1089,123 @@ int config_parse_bounding_set( return 0; } -int config_parse_limit( - const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { +static int rlim_parse_u64(const char *val, rlim_t *res) { + int r = 0; - struct rlimit **rl = data; - rlim_t v; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - rl += ltype; - - if (streq(rvalue, "infinity")) - v = RLIM_INFINITY; + if (streq(val, "infinity")) + *res = RLIM_INFINITY; else { uint64_t u; /* setrlimit(2) suggests rlim_t is always 64bit on Linux. */ assert_cc(sizeof(rlim_t) == sizeof(uint64_t)); - r = safe_atou64(rvalue, &u); + r = safe_atou64(val, &u); if (r >= 0 && u >= (uint64_t) RLIM_INFINITY) r = -ERANGE; - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse resource value, ignoring: %s", rvalue); - return 0; - } + if (r == 0) + *res = (rlim_t) u; + } + return r; +} + +static int rlim_parse_size(const char *val, rlim_t *res) { + int r = 0; - v = (rlim_t) u; + if (streq(val, "infinity")) + *res = RLIM_INFINITY; + else { + uint64_t u; + + r = parse_size(val, 1024, &u); + if (r >= 0 && u >= (uint64_t) RLIM_INFINITY) + r = -ERANGE; + if (r == 0) + *res = (rlim_t) u; } + return r; +} + +static int rlim_parse_sec(const char *val, rlim_t *res) { + int r = 0; + + if (streq(val, "infinity")) + *res = RLIM_INFINITY; + else { + usec_t t; + + r = parse_sec(val, &t); + if (r < 0) + return r; + if (t == USEC_INFINITY) + *res = RLIM_INFINITY; + else + *res = (rlim_t) (DIV_ROUND_UP(t, USEC_PER_SEC)); + + } + return r; +} + +static int rlim_parse_usec(const char *val, rlim_t *res) { + int r = 0; + + if (streq(val, "infinity")) + *res = RLIM_INFINITY; + else { + usec_t t; + + r = parse_time(val, &t, 1); + if (r < 0) + return r; + if (t == USEC_INFINITY) + *res = RLIM_INFINITY; + else + *res = (rlim_t) t; + } + return r; +} + +static int parse_rlimit_range( + const char *unit, + const char *filename, + unsigned line, + const char *value, + struct rlimit **rl, + int (*rlim_parser)(const char *, rlim_t *)) { + + const char *whole_value = value; + rlim_t soft, hard; + _cleanup_free_ char *sword = NULL, *hword = NULL; + int nwords, r; + + assert(value); + + /* <value> or <soft:hard> */ + nwords = extract_many_words(&value, ":", EXTRACT_DONT_COALESCE_SEPARATORS, &sword, &hword, NULL); + r = nwords < 0 ? nwords : nwords == 0 ? -EINVAL : 0; + + if (r == 0) + r = rlim_parser(sword, &soft); + if (r == 0 && nwords == 2) + r = rlim_parser(hword, &hard); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse resource value, ignoring: %s", whole_value); + return 0; + } + if (nwords == 2 && soft > hard) + return log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid resource value ("RLIM_FMT" > "RLIM_FMT"), ignoring: %s", soft, hard, whole_value); if (!*rl) { *rl = new(struct rlimit, 1); if (!*rl) return log_oom(); } - - (*rl)->rlim_cur = (*rl)->rlim_max = v; + (*rl)->rlim_cur = soft; + (*rl)->rlim_max = nwords == 2 ? hard : soft; return 0; } -int config_parse_bytes_limit( +int config_parse_limit( const char *unit, const char *filename, unsigned line, @@ -1104,8 +1218,6 @@ int config_parse_bytes_limit( void *userdata) { struct rlimit **rl = data; - rlim_t bytes; - int r; assert(filename); assert(lvalue); @@ -1113,31 +1225,30 @@ int config_parse_bytes_limit( assert(data); rl += ltype; + return parse_rlimit_range(unit, filename, line, rvalue, rl, rlim_parse_u64); +} - if (streq(rvalue, "infinity")) - bytes = RLIM_INFINITY; - else { - uint64_t u; - - r = parse_size(rvalue, 1024, &u); - if (r >= 0 && u >= (uint64_t) RLIM_INFINITY) - r = -ERANGE; - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse resource value, ignoring: %s", rvalue); - return 0; - } +int config_parse_bytes_limit( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { - bytes = (rlim_t) u; - } + struct rlimit **rl = data; - if (!*rl) { - *rl = new(struct rlimit, 1); - if (!*rl) - return log_oom(); - } + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); - (*rl)->rlim_cur = (*rl)->rlim_max = bytes; - return 0; + rl += ltype; + return parse_rlimit_range(unit, filename, line, rvalue, rl, rlim_parse_size); } int config_parse_sec_limit( @@ -1153,8 +1264,6 @@ int config_parse_sec_limit( void *userdata) { struct rlimit **rl = data; - rlim_t seconds; - int r; assert(filename); assert(lvalue); @@ -1162,35 +1271,9 @@ int config_parse_sec_limit( assert(data); rl += ltype; - - if (streq(rvalue, "infinity")) - seconds = RLIM_INFINITY; - else { - usec_t t; - - r = parse_sec(rvalue, &t); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse resource value, ignoring: %s", rvalue); - return 0; - } - - if (t == USEC_INFINITY) - seconds = RLIM_INFINITY; - else - seconds = (rlim_t) (DIV_ROUND_UP(t, USEC_PER_SEC)); - } - - if (!*rl) { - *rl = new(struct rlimit, 1); - if (!*rl) - return log_oom(); - } - - (*rl)->rlim_cur = (*rl)->rlim_max = seconds; - return 0; + return parse_rlimit_range(unit, filename, line, rvalue, rl, rlim_parse_sec); } - int config_parse_usec_limit( const char *unit, const char *filename, @@ -1204,8 +1287,6 @@ int config_parse_usec_limit( void *userdata) { struct rlimit **rl = data; - rlim_t useconds; - int r; assert(filename); assert(lvalue); @@ -1213,33 +1294,10 @@ int config_parse_usec_limit( assert(data); rl += ltype; + return parse_rlimit_range(unit, filename, line, rvalue, rl, rlim_parse_usec); +} - if (streq(rvalue, "infinity")) - useconds = RLIM_INFINITY; - else { - usec_t t; - - r = parse_time(rvalue, &t, 1); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse resource value, ignoring: %s", rvalue); - return 0; - } - - if (t == USEC_INFINITY) - useconds = RLIM_INFINITY; - else - useconds = (rlim_t) t; - } - - if (!*rl) { - *rl = new(struct rlimit, 1); - if (!*rl) - return log_oom(); - } - (*rl)->rlim_cur = (*rl)->rlim_max = useconds; - return 0; -} #ifdef HAVE_SYSV_COMPAT int config_parse_sysv_priority(const char *unit, @@ -1286,38 +1344,28 @@ int config_parse_exec_mount_flags(const char *unit, void *data, void *userdata) { - ExecContext *c = data; - const char *word, *state; - size_t l; + unsigned long flags = 0; + ExecContext *c = data; assert(filename); assert(lvalue); assert(rvalue); assert(data); - FOREACH_WORD_SEPARATOR(word, l, rvalue, ", ", state) { - _cleanup_free_ char *t; - - t = strndup(word, l); - if (!t) - return log_oom(); - - if (streq(t, "shared")) - flags = MS_SHARED; - else if (streq(t, "slave")) - flags = MS_SLAVE; - else if (streq(t, "private")) - flags = MS_PRIVATE; - else { - log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse mount flag %s, ignoring: %s", t, rvalue); - return 0; - } + if (streq(rvalue, "shared")) + flags = MS_SHARED; + else if (streq(rvalue, "slave")) + flags = MS_SLAVE; + else if (streq(rvalue, "private")) + flags = MS_PRIVATE; + else { + log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse mount flag %s, ignoring.", rvalue); + return 0; } - if (!isempty(state)) - log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring."); c->mount_flags = flags; + return 0; } @@ -2196,6 +2244,70 @@ int config_parse_environ(const char *unit, return 0; } +int config_parse_pass_environ(const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + const char *whole_rvalue = rvalue; + char*** passenv = data; + _cleanup_strv_free_ char **n = NULL; + size_t nlen = 0, nbufsize = 0; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + if (isempty(rvalue)) { + /* Empty assignment resets the list */ + *passenv = strv_free(*passenv); + return 0; + } + + for (;;) { + _cleanup_free_ char *word = NULL; + + r = extract_first_word(&rvalue, &word, WHITESPACE, EXTRACT_QUOTES); + if (r == 0) + break; + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, + "Trailing garbage in %s, ignoring: %s", lvalue, whole_rvalue); + break; + } + + if (!env_name_is_valid(word)) { + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Invalid environment name for %s, ignoring: %s", lvalue, word); + continue; + } + + if (!GREEDY_REALLOC(n, nbufsize, nlen + 2)) + return log_oom(); + n[nlen++] = word; + n[nlen] = NULL; + word = NULL; + } + + if (n) { + r = strv_extend_strv(passenv, n, true); + if (r < 0) + return r; + } + + return 0; +} + int config_parse_ip_tos(const char *unit, const char *filename, unsigned line, @@ -2908,12 +3020,11 @@ int config_parse_tasks_max( void *data, void *userdata) { - CGroupContext *c = data; - uint64_t u; + uint64_t *tasks_max = data, u; int r; if (isempty(rvalue) || streq(rvalue, "infinity")) { - c->tasks_max = (uint64_t) -1; + *tasks_max = (uint64_t) -1; return 0; } @@ -2923,7 +3034,7 @@ int config_parse_tasks_max( return 0; } - c->tasks_max = u; + *tasks_max = u; return 0; } diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h index 0cf821289c..a451fc164a 100644 --- a/src/core/load-fragment.h +++ b/src/core/load-fragment.h @@ -31,12 +31,14 @@ void unit_dump_config_items(FILE *f); int config_parse_warn_compat(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_unit_deps(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_obsolete_unit_deps(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_unit_string_printf(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_unit_strv_printf(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_unit_path_printf(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_unit_path_strv_printf(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_documentation(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_socket_listen(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_socket_protocol(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_socket_bind(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_exec_nice(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_exec_oom_score_adjust(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); @@ -84,6 +86,7 @@ int config_parse_syscall_filter(const char *unit, const char *filename, unsigned int config_parse_syscall_archs(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_syscall_errno(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_environ(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_pass_environ(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_unit_slice(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_cpu_shares(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_memory_limit(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); diff --git a/src/core/locale-setup.c b/src/core/locale-setup.c index bd632131b9..4c8d920389 100644 --- a/src/core/locale-setup.c +++ b/src/core/locale-setup.c @@ -24,12 +24,12 @@ #include "env-util.h" #include "fileio.h" +#include "locale-setup.h" #include "locale-util.h" #include "string-util.h" #include "strv.h" #include "util.h" #include "virt.h" -#include "locale-setup.h" int locale_setup(char ***environment) { char **add; diff --git a/src/core/macros.systemd.in b/src/core/macros.systemd.in index 8a0e44b58c..2cace3d3ba 100644 --- a/src/core/macros.systemd.in +++ b/src/core/macros.systemd.in @@ -43,7 +43,7 @@ if [ $1 -eq 1 ] ; then \ fi \ %{nil} -%systemd_user_post() %systemd_post --user --global %{?*} +%systemd_user_post() %{expand:%systemd_post \\--user \\--global %%{?*}} %systemd_preun() \ if [ $1 -eq 0 ] ; then \ diff --git a/src/core/main.c b/src/core/main.c index a86080642d..97f904b031 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -37,8 +37,8 @@ #include <valgrind/valgrind.h> #endif -#include "sd-daemon.h" #include "sd-bus.h" +#include "sd-daemon.h" #include "alloc-util.h" #include "architecture.h" @@ -125,7 +125,8 @@ static FILE* arg_serialization = NULL; static bool arg_default_cpu_accounting = false; static bool arg_default_blockio_accounting = false; static bool arg_default_memory_accounting = false; -static bool arg_default_tasks_accounting = false; +static bool arg_default_tasks_accounting = true; +static uint64_t arg_default_tasks_max = UINT64_C(512); static void pager_open_if_enabled(void) { @@ -657,7 +658,7 @@ static int parse_config_file(void) { { "Manager", "DefaultStartLimitInterval", config_parse_sec, 0, &arg_default_start_limit_interval }, { "Manager", "DefaultStartLimitBurst", config_parse_unsigned, 0, &arg_default_start_limit_burst }, { "Manager", "DefaultEnvironment", config_parse_environ, 0, &arg_default_environment }, - { "Manager", "DefaultLimitCPU", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_CPU] }, + { "Manager", "DefaultLimitCPU", config_parse_sec_limit, 0, &arg_default_rlimit[RLIMIT_CPU] }, { "Manager", "DefaultLimitFSIZE", config_parse_bytes_limit, 0, &arg_default_rlimit[RLIMIT_FSIZE] }, { "Manager", "DefaultLimitDATA", config_parse_bytes_limit, 0, &arg_default_rlimit[RLIMIT_DATA] }, { "Manager", "DefaultLimitSTACK", config_parse_bytes_limit, 0, &arg_default_rlimit[RLIMIT_STACK] }, @@ -672,11 +673,12 @@ static int parse_config_file(void) { { "Manager", "DefaultLimitMSGQUEUE", config_parse_bytes_limit, 0, &arg_default_rlimit[RLIMIT_MSGQUEUE] }, { "Manager", "DefaultLimitNICE", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_NICE] }, { "Manager", "DefaultLimitRTPRIO", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_RTPRIO] }, - { "Manager", "DefaultLimitRTTIME", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_RTTIME] }, + { "Manager", "DefaultLimitRTTIME", config_parse_usec_limit, 0, &arg_default_rlimit[RLIMIT_RTTIME] }, { "Manager", "DefaultCPUAccounting", config_parse_bool, 0, &arg_default_cpu_accounting }, { "Manager", "DefaultBlockIOAccounting", config_parse_bool, 0, &arg_default_blockio_accounting }, { "Manager", "DefaultMemoryAccounting", config_parse_bool, 0, &arg_default_memory_accounting }, { "Manager", "DefaultTasksAccounting", config_parse_bool, 0, &arg_default_tasks_accounting }, + { "Manager", "DefaultTasksMax", config_parse_tasks_max, 0, &arg_default_tasks_max }, {} }; @@ -712,6 +714,7 @@ static void manager_set_defaults(Manager *m) { m->default_blockio_accounting = arg_default_blockio_accounting; m->default_memory_accounting = arg_default_memory_accounting; m->default_tasks_accounting = arg_default_tasks_accounting; + m->default_tasks_max = arg_default_tasks_max; manager_set_default_rlimits(m, arg_default_rlimit); manager_environment_add(m, NULL, arg_default_environment); @@ -1744,11 +1747,13 @@ int main(int argc, char *argv[]) { manager_dump_units(m, stdout, "\t"); } - r = manager_add_job(m, JOB_START, target, JOB_ISOLATE, false, &error, &default_unit_job); + r = manager_add_job(m, JOB_START, target, JOB_ISOLATE, &error, &default_unit_job); if (r == -EPERM) { log_debug("Default target could not be isolated, starting instead: %s", bus_error_message(&error, r)); - r = manager_add_job(m, JOB_START, target, JOB_REPLACE, false, &error, &default_unit_job); + sd_bus_error_free(&error); + + r = manager_add_job(m, JOB_START, target, JOB_REPLACE, &error, &default_unit_job); if (r < 0) { log_emergency("Failed to start default target: %s", bus_error_message(&error, r)); error_message = "Failed to start default target"; diff --git a/src/core/manager.c b/src/core/manager.c index 7c3a020c4a..edff6758c5 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -486,7 +486,7 @@ static int manager_setup_signals(Manager *m) { * later than notify_fd processing, so that the notify * processing can still figure out to which process/service a * message belongs, before we reap the process. */ - r = sd_event_source_set_priority(m->signal_event_source, -5); + r = sd_event_source_set_priority(m->signal_event_source, SD_EVENT_PRIORITY_NORMAL-5); if (r < 0) return r; @@ -577,6 +577,8 @@ int manager_new(ManagerRunningAs running_as, bool test_run, Manager **_m) { m->running_as = running_as; 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); /* Prepare log fields we can use for structured logging */ m->unit_log_field = unit_log_fields[running_as]; @@ -734,7 +736,7 @@ static int manager_setup_notify(Manager *m) { /* Process signals a bit earlier than SIGCHLD, so that we can * still identify to which service an exit message belongs */ - r = sd_event_source_set_priority(m->notify_event_source, -7); + r = sd_event_source_set_priority(m->notify_event_source, SD_EVENT_PRIORITY_NORMAL-7); if (r < 0) return log_error_errno(r, "Failed to set priority of notify event source: %m"); @@ -993,8 +995,7 @@ Manager* manager_free(Manager *m) { return NULL; } -int manager_enumerate(Manager *m) { - int r = 0; +void manager_enumerate(Manager *m) { UnitType c; assert(m); @@ -1002,8 +1003,6 @@ int manager_enumerate(Manager *m) { /* Let's ask every type to load all units from disk/kernel * that it might know */ for (c = 0; c < _UNIT_TYPE_MAX; c++) { - int q; - if (!unit_type_supported(c)) { log_debug("Unit type .%s is not supported on this system.", unit_type_to_string(c)); continue; @@ -1012,13 +1011,10 @@ int manager_enumerate(Manager *m) { if (!unit_vtable[c]->enumerate) continue; - q = unit_vtable[c]->enumerate(m); - if (q < 0) - r = q; + unit_vtable[c]->enumerate(m); } manager_dispatch_load_queue(m); - return r; } static void manager_coldplug(Manager *m) { @@ -1100,10 +1096,9 @@ fail: } -static int manager_distribute_fds(Manager *m, FDSet *fds) { - Unit *u; +static void manager_distribute_fds(Manager *m, FDSet *fds) { Iterator i; - int r; + Unit *u; assert(m); @@ -1112,14 +1107,11 @@ static int manager_distribute_fds(Manager *m, FDSet *fds) { if (fdset_size(fds) <= 0) break; - if (UNIT_VTABLE(u)->distribute_fds) { - r = UNIT_VTABLE(u)->distribute_fds(u, fds); - if (r < 0) - return r; - } - } + if (!UNIT_VTABLE(u)->distribute_fds) + continue; - return 0; + UNIT_VTABLE(u)->distribute_fds(u, fds); + } } int manager_startup(Manager *m, FILE *serialization, FDSet *fds) { @@ -1152,7 +1144,7 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) { /* First, enumerate what we can from all config files */ dual_timestamp_get(&m->units_load_start_timestamp); - r = manager_enumerate(m); + manager_enumerate(m); dual_timestamp_get(&m->units_load_finish_timestamp); /* Second, deserialize if there is something to deserialize */ @@ -1163,11 +1155,7 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) { * useful to allow container managers to pass some file * descriptors to us pre-initialized. This enables * socket-based activation of entire containers. */ - if (fdset_size(fds) > 0) { - q = manager_distribute_fds(m, fds); - if (q < 0 && r == 0) - r = q; - } + manager_distribute_fds(m, fds); /* We might have deserialized the notify fd, but if we didn't * then let's create the bus now */ @@ -1197,7 +1185,7 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) { return r; } -int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, bool override, sd_bus_error *e, Job **_ret) { +int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, sd_bus_error *e, Job **_ret) { int r; Transaction *tr; @@ -1220,7 +1208,7 @@ int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, bool ove if (!tr) return -ENOMEM; - r = transaction_add_job_and_dependencies(tr, type, unit, NULL, true, override, false, + r = transaction_add_job_and_dependencies(tr, type, unit, NULL, true, false, mode == JOB_IGNORE_DEPENDENCIES || mode == JOB_IGNORE_REQUIREMENTS, mode == JOB_IGNORE_DEPENDENCIES, e); if (r < 0) @@ -1252,7 +1240,7 @@ tr_abort: return r; } -int manager_add_job_by_name(Manager *m, JobType type, const char *name, JobMode mode, bool override, sd_bus_error *e, Job **_ret) { +int manager_add_job_by_name(Manager *m, JobType type, const char *name, JobMode mode, sd_bus_error *e, Job **ret) { Unit *unit; int r; @@ -1265,7 +1253,23 @@ int manager_add_job_by_name(Manager *m, JobType type, const char *name, JobMode if (r < 0) return r; - return manager_add_job(m, type, unit, mode, override, e, _ret); + return manager_add_job(m, type, unit, mode, e, ret); +} + +int manager_add_job_by_name_and_warn(Manager *m, JobType type, const char *name, JobMode mode, Job **ret) { + _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; + int r; + + assert(m); + assert(type < _JOB_TYPE_MAX); + assert(name); + assert(mode < _JOB_MODE_MAX); + + r = manager_add_job_by_name(m, type, name, mode, &error, ret); + if (r < 0) + return log_warning_errno(r, "Failed to enqueue %s job for %s: %s", job_mode_to_string(mode), name, bus_error_message(&error, r)); + + return r; } Job *manager_get_job(Manager *m, uint32_t id) { @@ -1701,7 +1705,7 @@ static int manager_start_target(Manager *m, const char *name, JobMode mode) { log_debug("Activating special unit %s", name); - r = manager_add_job_by_name(m, JOB_START, name, mode, true, &error, NULL); + r = manager_add_job_by_name(m, JOB_START, name, mode, &error, NULL); if (r < 0) log_error("Failed to enqueue %s job: %s", name, bus_error_message(&error, r)); @@ -2554,9 +2558,7 @@ int manager_reload(Manager *m) { manager_build_unit_path_cache(m); /* First, enumerate what we can from all config files */ - q = manager_enumerate(m); - if (q < 0 && r >= 0) - r = q; + manager_enumerate(m); /* Second, deserialize our stored data */ q = manager_deserialize(m, f, fds); diff --git a/src/core/manager.h b/src/core/manager.h index 38d2770e97..f6903a5c34 100644 --- a/src/core/manager.h +++ b/src/core/manager.h @@ -21,12 +21,13 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <libmount.h> #include <stdbool.h> #include <stdio.h> -#include <libmount.h> #include "sd-bus.h" #include "sd-event.h" + #include "cgroup-util.h" #include "fdset.h" #include "hashmap.h" @@ -141,8 +142,6 @@ struct Manager { sd_event_source *jobs_in_progress_event_source; - unsigned n_snapshots; - LookupPaths lookup_paths; Set *unit_path_cache; @@ -262,6 +261,7 @@ struct Manager { bool default_blockio_accounting; bool default_tasks_accounting; + uint64_t default_tasks_max; usec_t default_timer_accuracy_usec; struct rlimit *rlimit[_RLIMIT_MAX]; @@ -316,7 +316,7 @@ struct Manager { int manager_new(ManagerRunningAs running_as, bool test_run, Manager **m); Manager* manager_free(Manager *m); -int manager_enumerate(Manager *m); +void manager_enumerate(Manager *m); int manager_startup(Manager *m, FILE *serialization, FDSet *fds); Job *manager_get_job(Manager *m, uint32_t id); @@ -328,8 +328,9 @@ int manager_load_unit_prepare(Manager *m, const char *name, const char *path, sd int manager_load_unit(Manager *m, const char *name, const char *path, sd_bus_error *e, Unit **_ret); int manager_load_unit_from_dbus_path(Manager *m, const char *s, sd_bus_error *e, Unit **_u); -int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, bool force, sd_bus_error *e, Job **_ret); -int manager_add_job_by_name(Manager *m, JobType type, const char *name, JobMode mode, bool force, sd_bus_error *e, Job **_ret); +int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, sd_bus_error *e, Job **_ret); +int manager_add_job_by_name(Manager *m, JobType type, const char *name, JobMode mode, sd_bus_error *e, Job **_ret); +int manager_add_job_by_name_and_warn(Manager *m, JobType type, const char *name, JobMode mode, Job **ret); void manager_dump_units(Manager *s, FILE *f, const char *prefix); void manager_dump_jobs(Manager *s, FILE *f, const char *prefix); diff --git a/src/core/mount-setup.c b/src/core/mount-setup.c index b2596d1cd1..2b8d590ed1 100644 --- a/src/core/mount-setup.c +++ b/src/core/mount-setup.c @@ -19,11 +19,11 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <sys/mount.h> #include <errno.h> +#include <ftw.h> #include <stdlib.h> +#include <sys/mount.h> #include <unistd.h> -#include <ftw.h> #include "alloc-util.h" #include "bus-util.h" diff --git a/src/core/mount.c b/src/core/mount.c index 950d5d76d5..9b44357e90 100644 --- a/src/core/mount.c +++ b/src/core/mount.c @@ -41,7 +41,6 @@ #include "parse-util.h" #include "path-util.h" #include "process-util.h" -#include "smack-util.h" #include "special.h" #include "string-table.h" #include "string-util.h" @@ -211,7 +210,6 @@ static void mount_done(Unit *u) { assert(m); m->where = mfree(m->where); - m->smack_fs_root_label = mfree(m->smack_fs_root_label); mount_parameters_done(&m->parameters_proc_self_mountinfo); mount_parameters_done(&m->parameters_fragment); @@ -387,12 +385,15 @@ static bool should_umount(Mount *m) { } static int mount_add_default_dependencies(Mount *m) { - const char *after, *after2, *online; MountParameters *p; + const char *after; int r; assert(m); + if (!UNIT(m)->default_dependencies) + return 0; + if (UNIT(m)->manager->running_as != MANAGER_SYSTEM) return 0; @@ -413,30 +414,34 @@ static int mount_add_default_dependencies(Mount *m) { return 0; if (mount_is_network(p)) { - after = SPECIAL_REMOTE_FS_PRE_TARGET; - after2 = SPECIAL_NETWORK_TARGET; - online = SPECIAL_NETWORK_ONLINE_TARGET; - } else { - after = SPECIAL_LOCAL_FS_PRE_TARGET; - after2 = NULL; - online = NULL; - } + /* We order ourselves after network.target. This is + * primarily useful at shutdown: services that take + * down the network should order themselves before + * network.target, so that they are shut down only + * after this mount unit is stopped. */ - r = unit_add_dependency_by_name(UNIT(m), UNIT_AFTER, after, NULL, true); - if (r < 0) - return r; - - if (after2) { - r = unit_add_dependency_by_name(UNIT(m), UNIT_AFTER, after2, NULL, true); + r = unit_add_dependency_by_name(UNIT(m), UNIT_AFTER, SPECIAL_NETWORK_TARGET, NULL, true); if (r < 0) return r; - } - if (online) { - r = unit_add_two_dependencies_by_name(UNIT(m), UNIT_WANTS, UNIT_AFTER, online, NULL, true); + /* We pull in network-online.target, and order + * ourselves after it. This is useful at start-up to + * actively pull in tools that want to be started + * before we start mounting network file systems, and + * whose purpose it is to delay this until the network + * is "up". */ + + r = unit_add_two_dependencies_by_name(UNIT(m), UNIT_WANTS, UNIT_AFTER, SPECIAL_NETWORK_ONLINE_TARGET, NULL, true); if (r < 0) return r; - } + + after = SPECIAL_REMOTE_FS_PRE_TARGET; + } else + after = SPECIAL_LOCAL_FS_PRE_TARGET; + + r = unit_add_dependency_by_name(UNIT(m), UNIT_AFTER, after, NULL, true); + if (r < 0) + return r; if (should_umount(m)) { r = unit_add_two_dependencies_by_name(UNIT(m), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, NULL, true); @@ -533,11 +538,9 @@ static int mount_add_extras(Mount *m) { if (r < 0) return r; - if (u->default_dependencies) { - r = mount_add_default_dependencies(m); - if (r < 0) - return r; - } + r = mount_add_default_dependencies(m); + if (r < 0) + return r; return 0; } @@ -677,8 +680,7 @@ static void mount_dump(Unit *u, FILE *f, const char *prefix) { "%sOptions: %s\n" "%sFrom /proc/self/mountinfo: %s\n" "%sFrom fragment: %s\n" - "%sDirectoryMode: %04o\n" - "%sSmackFileSystemRootLabel: %s\n", + "%sDirectoryMode: %04o\n", prefix, mount_state_to_string(m->state), prefix, mount_result_to_string(m->result), prefix, m->where, @@ -687,8 +689,7 @@ static void mount_dump(Unit *u, FILE *f, const char *prefix) { prefix, p ? strna(p->options) : "n/a", prefix, yes_no(m->from_proc_self_mountinfo), prefix, yes_no(m->from_fragment), - prefix, m->directory_mode, - prefix, strna(m->smack_fs_root_label)); + prefix, m->directory_mode); if (m->control_pid > 0) fprintf(f, @@ -865,29 +866,9 @@ fail: mount_enter_mounted(m, MOUNT_FAILURE_RESOURCES); } -static int mount_get_opts(Mount *m, char **_opts) { - int r; - char *o = NULL, *opts = NULL; - - r = fstab_filter_options(m->parameters_fragment.options, - "nofail\0" "noauto\0" "auto\0", NULL, NULL, &o); - if (r < 0) - return r; - - if (mac_smack_use() && m->smack_fs_root_label) { - if (!isempty(o)) { - opts = strjoin(o, ",", "smackfsroot=", m->smack_fs_root_label, NULL); - free(o); - } else - opts = strjoin("smackfsroot=", m->smack_fs_root_label, NULL); - - if (!opts) - return -ENOMEM; - } else - opts = o; - - *_opts = opts; - return 0; +static int mount_get_opts(Mount *m, char **ret) { + return fstab_filter_options(m->parameters_fragment.options, + "nofail\0" "noauto\0" "auto\0", NULL, NULL, ret); } static void mount_enter_mounting(Mount *m) { @@ -1596,7 +1577,7 @@ static int mount_get_timeout(Unit *u, uint64_t *timeout) { return 1; } -static int mount_enumerate(Manager *m) { +static void mount_enumerate(Manager *m) { int r; assert(m); @@ -1608,29 +1589,40 @@ static int mount_enumerate(Manager *m) { m->mount_monitor = mnt_new_monitor(); if (!m->mount_monitor) { - r = -ENOMEM; + log_oom(); goto fail; } r = mnt_monitor_enable_kernel(m->mount_monitor, 1); - if (r < 0) + if (r < 0) { + log_error_errno(r, "Failed to enable watching of kernel mount events: %m"); goto fail; + } + r = mnt_monitor_enable_userspace(m->mount_monitor, 1, NULL); - if (r < 0) + if (r < 0) { + log_error_errno(r, "Failed to enable watching of userspace mount events: %m"); goto fail; + } /* mnt_unref_monitor() will close the fd */ fd = r = mnt_monitor_get_fd(m->mount_monitor); - if (r < 0) + if (r < 0) { + log_error_errno(r, "Failed to acquire watch file descriptor: %m"); goto fail; + } r = sd_event_add_io(m->event, &m->mount_event_source, fd, EPOLLIN, mount_dispatch_io, m); - if (r < 0) + if (r < 0) { + log_error_errno(r, "Failed to watch mount file descriptor: %m"); goto fail; + } r = sd_event_source_set_priority(m->mount_event_source, -10); - if (r < 0) + if (r < 0) { + log_error_errno(r, "Failed to adjust mount watch priority: %m"); goto fail; + } (void) sd_event_source_set_description(m->mount_event_source, "mount-monitor-dispatch"); } @@ -1639,11 +1631,10 @@ static int mount_enumerate(Manager *m) { if (r < 0) goto fail; - return 0; + return; fail: mount_shutdown(m); - return r; } static int mount_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata) { diff --git a/src/core/mount.h b/src/core/mount.h index b344b5aa13..9f78aa9075 100644 --- a/src/core/mount.h +++ b/src/core/mount.h @@ -23,8 +23,8 @@ typedef struct Mount Mount; -#include "kill.h" #include "execute.h" +#include "kill.h" typedef enum MountExecCommand { MOUNT_EXEC_MOUNT, @@ -71,7 +71,6 @@ struct Mount { bool reset_cpu_usage:1; bool sloppy_options; - char *smack_fs_root_label; MountResult result; MountResult reload_result; diff --git a/src/core/path.c b/src/core/path.c index 35e1753583..02fb134bb9 100644 --- a/src/core/path.c +++ b/src/core/path.c @@ -315,20 +315,20 @@ static int path_add_default_dependencies(Path *p) { assert(p); - r = unit_add_dependency_by_name(UNIT(p), UNIT_BEFORE, - SPECIAL_PATHS_TARGET, NULL, true); + if (!UNIT(p)->default_dependencies) + return 0; + + r = unit_add_dependency_by_name(UNIT(p), UNIT_BEFORE, SPECIAL_PATHS_TARGET, NULL, true); if (r < 0) return r; if (UNIT(p)->manager->running_as == MANAGER_SYSTEM) { - r = unit_add_two_dependencies_by_name(UNIT(p), UNIT_AFTER, UNIT_REQUIRES, - SPECIAL_SYSINIT_TARGET, NULL, true); + r = unit_add_two_dependencies_by_name(UNIT(p), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true); if (r < 0) return r; } - return unit_add_two_dependencies_by_name(UNIT(p), UNIT_BEFORE, UNIT_CONFLICTS, - SPECIAL_SHUTDOWN_TARGET, NULL, true); + return unit_add_two_dependencies_by_name(UNIT(p), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true); } static int path_load(Unit *u) { @@ -360,11 +360,9 @@ static int path_load(Unit *u) { if (r < 0) return r; - if (UNIT(p)->default_dependencies) { - r = path_add_default_dependencies(p); - if (r < 0) - return r; - } + r = path_add_default_dependencies(p); + if (r < 0) + return r; } return path_verify(p); @@ -476,8 +474,7 @@ static void path_enter_running(Path *p) { if (unit_stop_pending(UNIT(p))) return; - r = manager_add_job(UNIT(p)->manager, JOB_START, UNIT_TRIGGER(UNIT(p)), - JOB_REPLACE, true, &error, NULL); + r = manager_add_job(UNIT(p)->manager, JOB_START, UNIT_TRIGGER(UNIT(p)), JOB_REPLACE, &error, NULL); if (r < 0) goto fail; diff --git a/src/core/scope.c b/src/core/scope.c index 6bacb226e8..1953af1f88 100644 --- a/src/core/scope.c +++ b/src/core/scope.c @@ -54,7 +54,6 @@ static void scope_init(Unit *u) { s->timeout_stop_usec = u->manager->default_timeout_stop_usec; UNIT(s)->ignore_on_isolate = true; - UNIT(s)->ignore_on_snapshot = true; } static void scope_done(Unit *u) { @@ -123,6 +122,9 @@ static int scope_add_default_dependencies(Scope *s) { assert(s); + if (!UNIT(s)->default_dependencies) + return 0; + /* Make sure scopes are unloaded on shutdown */ r = unit_add_two_dependencies_by_name( UNIT(s), @@ -174,11 +176,9 @@ static int scope_load(Unit *u) { if (r < 0) return r; - if (u->default_dependencies) { - 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); } @@ -402,15 +402,10 @@ static bool scope_check_gc(Unit *u) { /* Never clean up scopes that still have a process around, * even if the scope is formally dead. */ - if (u->cgroup_path) { - int r; + if (!u->cgroup_path) + return false; - r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path); - if (r <= 0) - return true; - } - - return false; + return cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path) <= 0; } static void scope_notify_cgroup_empty_event(Unit *u) { @@ -510,7 +505,7 @@ _pure_ static const char *scope_sub_state_to_string(Unit *u) { return scope_state_to_string(SCOPE(u)->state); } -static int scope_enumerate(Manager *m) { +static void scope_enumerate(Manager *m) { Unit *u; int r; @@ -524,13 +519,16 @@ static int scope_enumerate(Manager *m) { u = manager_get_unit(m, SPECIAL_INIT_SCOPE); if (!u) { u = unit_new(m, sizeof(Scope)); - if (!u) - return log_oom(); + if (!u) { + log_oom(); + return; + } r = unit_add_name(u, SPECIAL_INIT_SCOPE); if (r < 0) { unit_free(u); - return log_error_errno(r, "Failed to add init.scope name"); + log_error_errno(r, "Failed to add init.scope name"); + return; } } @@ -551,8 +549,6 @@ static int scope_enumerate(Manager *m) { unit_add_to_load_queue(u); unit_add_to_dbus_queue(u); - - return 0; } static const char* const scope_result_table[_SCOPE_RESULT_MAX] = { @@ -576,6 +572,7 @@ const UnitVTable scope_vtable = { .no_alias = true, .no_instances = true, + .can_transient = true, .init = scope_init, .load = scope_load, @@ -610,7 +607,5 @@ const UnitVTable scope_vtable = { .bus_set_property = bus_scope_set_property, .bus_commit_properties = bus_scope_commit_properties, - .can_transient = true, - .enumerate = scope_enumerate, }; diff --git a/src/core/selinux-access.c b/src/core/selinux-access.c index 4bcdd27389..8856927c88 100644 --- a/src/core/selinux-access.c +++ b/src/core/selinux-access.c @@ -23,10 +23,10 @@ #ifdef HAVE_SELINUX -#include <stdio.h> #include <errno.h> -#include <selinux/selinux.h> #include <selinux/avc.h> +#include <selinux/selinux.h> +#include <stdio.h> #ifdef HAVE_AUDIT #include <libaudit.h> #endif diff --git a/src/core/selinux-access.h b/src/core/selinux-access.h index 30725521cb..3566ba529f 100644 --- a/src/core/selinux-access.h +++ b/src/core/selinux-access.h @@ -22,6 +22,7 @@ ***/ #include "sd-bus.h" + #include "bus-util.h" #include "manager.h" diff --git a/src/core/selinux-setup.c b/src/core/selinux-setup.c index d9b00fb95c..d4757e0853 100644 --- a/src/core/selinux-setup.c +++ b/src/core/selinux-setup.c @@ -29,10 +29,10 @@ #include "log.h" #include "macro.h" +#include "selinux-setup.h" #include "selinux-util.h" #include "string-util.h" #include "util.h" -#include "selinux-setup.h" #ifdef HAVE_SELINUX _printf_(2,3) diff --git a/src/core/service.c b/src/core/service.c index 586eddd99a..c27b70fa3c 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -515,15 +515,38 @@ static int service_add_default_dependencies(Service *s) { assert(s); + if (!UNIT(s)->default_dependencies) + return 0; + /* Add a number of automatic dependencies useful for the * majority of services. */ - /* First, pull in base system */ - r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_BASIC_TARGET, NULL, true); + if (UNIT(s)->manager->running_as == MANAGER_SYSTEM) { + /* First, pull in the really early boot stuff, and + * require it, so that we fail if we can't acquire + * it. */ + + r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true); + if (r < 0) + return r; + } else { + + /* In the --user instance there's no sysinit.target, + * in that case require basic.target instead. */ + + r = unit_add_dependency_by_name(UNIT(s), UNIT_REQUIRES, SPECIAL_BASIC_TARGET, NULL, true); + if (r < 0) + return r; + } + + /* Second, if the rest of the base system is in the same + * transaction, order us after it, but do not pull it in or + * even require it. */ + r = unit_add_dependency_by_name(UNIT(s), UNIT_AFTER, SPECIAL_BASIC_TARGET, NULL, true); if (r < 0) return r; - /* Second, activate normal shutdown */ + /* Third, add us in for normal shutdown. */ return unit_add_two_dependencies_by_name(UNIT(s), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true); } @@ -545,6 +568,43 @@ static void service_fix_output(Service *s) { s->exec_context.std_output = UNIT(s)->manager->default_std_output; } +static int service_setup_bus_name(Service *s) { + int r; + + assert(s); + + if (!s->bus_name) + return 0; + + if (is_kdbus_available()) { + const char *n; + + n = strjoina(s->bus_name, ".busname"); + r = unit_add_dependency_by_name(UNIT(s), UNIT_AFTER, n, NULL, true); + if (r < 0) + return log_unit_error_errno(UNIT(s), r, "Failed to add dependency to .busname unit: %m"); + + } else { + /* If kdbus is not available, we know the dbus socket is required, hence pull it in, and require it */ + r = unit_add_dependency_by_name(UNIT(s), UNIT_REQUIRES, SPECIAL_DBUS_SOCKET, NULL, true); + if (r < 0) + return log_unit_error_errno(UNIT(s), r, "Failed to add dependency on " SPECIAL_DBUS_SOCKET ": %m"); + } + + /* Regardless if kdbus is used or not, we always want to be ordered against dbus.socket if both are in the transaction. */ + r = unit_add_dependency_by_name(UNIT(s), UNIT_AFTER, SPECIAL_DBUS_SOCKET, NULL, true); + if (r < 0) + return log_unit_error_errno(UNIT(s), r, "Failed to add dependency on " SPECIAL_DBUS_SOCKET ": %m"); + + r = unit_watch_bus_name(UNIT(s), s->bus_name); + if (r == -EEXIST) + return log_unit_error_errno(UNIT(s), r, "Two services allocated for the same bus name %s, refusing operation.", s->bus_name); + if (r < 0) + return log_unit_error_errno(UNIT(s), r, "Cannot watch bus name %s: %m", s->bus_name); + + return 0; +} + static int service_add_extras(Service *s) { int r; @@ -584,26 +644,13 @@ static int service_add_extras(Service *s) { if (s->watchdog_usec > 0 && s->notify_access == NOTIFY_NONE) s->notify_access = NOTIFY_MAIN; - if (s->bus_name) { - const char *n; - - n = strjoina(s->bus_name, ".busname"); - r = unit_add_dependency_by_name(UNIT(s), UNIT_AFTER, n, NULL, true); - if (r < 0) - return r; - - r = unit_watch_bus_name(UNIT(s), s->bus_name); - if (r == -EEXIST) - return log_unit_error_errno(UNIT(s), r, "Two services allocated for the same bus name %s, refusing operation.", s->bus_name); - if (r < 0) - return log_unit_error_errno(UNIT(s), r, "Cannot watch bus name %s: %m", s->bus_name); - } + r = service_add_default_dependencies(s); + if (r < 0) + return r; - if (UNIT(s)->default_dependencies) { - r = service_add_default_dependencies(s); - if (r < 0) - return r; - } + r = service_setup_bus_name(s); + if (r < 0) + return r; return 0; } @@ -1802,7 +1849,7 @@ static void service_enter_restart(Service *s) { * restarted. We use JOB_RESTART (instead of the more obvious * JOB_START) here so that those dependency jobs will be added * as well. */ - r = manager_add_job(UNIT(s)->manager, JOB_RESTART, UNIT(s), JOB_FAIL, false, &error, NULL); + r = manager_add_job(UNIT(s)->manager, JOB_RESTART, UNIT(s), JOB_FAIL, &error, NULL); if (r < 0) goto fail; @@ -2365,14 +2412,6 @@ static bool service_check_gc(Unit *u) { return false; } -_pure_ static bool service_check_snapshot(Unit *u) { - Service *s = SERVICE(u); - - assert(s); - - return s->socket_fd < 0; -} - static int service_retry_pid_file(Service *s) { int r; @@ -3294,7 +3333,6 @@ const UnitVTable service_vtable = { .sub_state_to_string = service_sub_state_to_string, .check_gc = service_check_gc, - .check_snapshot = service_check_snapshot, .sigchld_event = service_sigchld_event, diff --git a/src/core/service.h b/src/core/service.h index e765668247..d0faad88e0 100644 --- a/src/core/service.h +++ b/src/core/service.h @@ -24,10 +24,10 @@ typedef struct Service Service; typedef struct ServiceFDStore ServiceFDStore; +#include "exit-status.h" +#include "kill.h" #include "path.h" #include "ratelimit.h" -#include "kill.h" -#include "exit-status.h" typedef enum ServiceRestart { SERVICE_RESTART_NO, diff --git a/src/core/slice.c b/src/core/slice.c index 4602144150..06ac6f8450 100644 --- a/src/core/slice.c +++ b/src/core/slice.c @@ -24,12 +24,12 @@ #include "alloc-util.h" #include "dbus-slice.h" #include "log.h" +#include "slice.h" #include "special.h" #include "string-util.h" #include "strv.h" #include "unit-name.h" #include "unit.h" -#include "slice.h" static const UnitActiveState state_translation_table[_SLICE_STATE_MAX] = { [SLICE_DEAD] = UNIT_INACTIVE, @@ -85,6 +85,9 @@ static int slice_add_default_dependencies(Slice *s) { assert(s); + if (!UNIT(s)->default_dependencies) + return 0; + /* Make sure slices are unloaded on shutdown */ r = unit_add_two_dependencies_by_name( UNIT(s), @@ -96,7 +99,6 @@ static int slice_add_default_dependencies(Slice *s) { return 0; } - static int slice_verify(Slice *s) { _cleanup_free_ char *parent = NULL; int r; @@ -144,11 +146,9 @@ static int slice_load(Unit *u) { if (r < 0) return r; - if (u->default_dependencies) { - r = slice_add_default_dependencies(s); - if (r < 0) - return r; - } + r = slice_add_default_dependencies(s); + if (r < 0) + return r; } return slice_verify(s); @@ -255,7 +255,7 @@ _pure_ static const char *slice_sub_state_to_string(Unit *u) { return slice_state_to_string(SLICE(u)->state); } -static int slice_enumerate(Manager *m) { +static void slice_enumerate(Manager *m) { Unit *u; int r; @@ -264,13 +264,16 @@ static int slice_enumerate(Manager *m) { u = manager_get_unit(m, SPECIAL_ROOT_SLICE); if (!u) { u = unit_new(m, sizeof(Slice)); - if (!u) - return log_oom(); + if (!u) { + log_oom(); + return; + } r = unit_add_name(u, SPECIAL_ROOT_SLICE); if (r < 0) { unit_free(u); - return log_error_errno(r, "Failed to add -.slice name"); + log_error_errno(r, "Failed to add -.slice name"); + return; } } @@ -288,8 +291,6 @@ static int slice_enumerate(Manager *m) { unit_add_to_load_queue(u); unit_add_to_dbus_queue(u); - - return 0; } const UnitVTable slice_vtable = { @@ -304,6 +305,7 @@ const UnitVTable slice_vtable = { .no_alias = true, .no_instances = true, + .can_transient = true, .load = slice_load, diff --git a/src/core/snapshot.c b/src/core/snapshot.c deleted file mode 100644 index ba3135f401..0000000000 --- a/src/core/snapshot.c +++ /dev/null @@ -1,303 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -/*** - This file is part of systemd. - - Copyright 2010 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 <errno.h> - -#include "alloc-util.h" -#include "bus-common-errors.h" -#include "dbus-snapshot.h" -#include "parse-util.h" -#include "parse-util.h" -#include "snapshot.h" -#include "string-util.h" -#include "unit-name.h" -#include "unit.h" - -static const UnitActiveState state_translation_table[_SNAPSHOT_STATE_MAX] = { - [SNAPSHOT_DEAD] = UNIT_INACTIVE, - [SNAPSHOT_ACTIVE] = UNIT_ACTIVE -}; - -static void snapshot_init(Unit *u) { - Snapshot *s = SNAPSHOT(u); - - assert(s); - assert(UNIT(s)->load_state == UNIT_STUB); - - UNIT(s)->ignore_on_isolate = true; - UNIT(s)->ignore_on_snapshot = true; - UNIT(s)->allow_isolate = true; -} - -static void snapshot_set_state(Snapshot *s, SnapshotState state) { - SnapshotState old_state; - assert(s); - - old_state = s->state; - s->state = state; - - if (state != old_state) - log_unit_debug(UNIT(s), "Changed %s -> %s", snapshot_state_to_string(old_state), snapshot_state_to_string(state)); - - unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state], true); -} - -static int snapshot_load(Unit *u) { - Snapshot *s = SNAPSHOT(u); - - assert(u); - assert(u->load_state == UNIT_STUB); - - /* Make sure that only snapshots created via snapshot_create() - * can be loaded */ - if (!u->transient && UNIT(s)->manager->n_reloading <= 0) - return -ENOENT; - - u->load_state = UNIT_LOADED; - return 0; -} - -static int snapshot_coldplug(Unit *u) { - Snapshot *s = SNAPSHOT(u); - - assert(s); - assert(s->state == SNAPSHOT_DEAD); - - if (s->deserialized_state != s->state) - snapshot_set_state(s, s->deserialized_state); - - return 0; -} - -static void snapshot_dump(Unit *u, FILE *f, const char *prefix) { - Snapshot *s = SNAPSHOT(u); - - assert(s); - assert(f); - - fprintf(f, - "%sSnapshot State: %s\n" - "%sClean Up: %s\n", - prefix, snapshot_state_to_string(s->state), - prefix, yes_no(s->cleanup)); -} - -static int snapshot_start(Unit *u) { - Snapshot *s = SNAPSHOT(u); - - assert(s); - assert(s->state == SNAPSHOT_DEAD); - - snapshot_set_state(s, SNAPSHOT_ACTIVE); - - if (s->cleanup) - unit_add_to_cleanup_queue(u); - - return 1; -} - -static int snapshot_stop(Unit *u) { - Snapshot *s = SNAPSHOT(u); - - assert(s); - assert(s->state == SNAPSHOT_ACTIVE); - - snapshot_set_state(s, SNAPSHOT_DEAD); - return 1; -} - -static int snapshot_serialize(Unit *u, FILE *f, FDSet *fds) { - Snapshot *s = SNAPSHOT(u); - Unit *other; - Iterator i; - - assert(s); - assert(f); - assert(fds); - - unit_serialize_item(u, f, "state", snapshot_state_to_string(s->state)); - unit_serialize_item(u, f, "cleanup", yes_no(s->cleanup)); - SET_FOREACH(other, u->dependencies[UNIT_WANTS], i) - unit_serialize_item(u, f, "wants", other->id); - - return 0; -} - -static int snapshot_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) { - Snapshot *s = SNAPSHOT(u); - int r; - - assert(u); - assert(key); - assert(value); - assert(fds); - - if (streq(key, "state")) { - SnapshotState state; - - state = snapshot_state_from_string(value); - if (state < 0) - log_unit_debug(u, "Failed to parse state value: %s", value); - else - s->deserialized_state = state; - - } else if (streq(key, "cleanup")) { - - r = parse_boolean(value); - if (r < 0) - log_unit_debug(u, "Failed to parse cleanup value: %s", value); - else - s->cleanup = r; - - } else if (streq(key, "wants")) { - - r = unit_add_two_dependencies_by_name(u, UNIT_AFTER, UNIT_WANTS, value, NULL, true); - if (r < 0) - return r; - } else - log_unit_debug(u, "Unknown serialization key: %s", key); - - return 0; -} - -_pure_ static UnitActiveState snapshot_active_state(Unit *u) { - assert(u); - - return state_translation_table[SNAPSHOT(u)->state]; -} - -_pure_ static const char *snapshot_sub_state_to_string(Unit *u) { - assert(u); - - return snapshot_state_to_string(SNAPSHOT(u)->state); -} - -int snapshot_create(Manager *m, const char *name, bool cleanup, sd_bus_error *e, Snapshot **_s) { - _cleanup_free_ char *n = NULL; - Unit *other, *u = NULL; - Iterator i; - int r; - const char *k; - - assert(m); - assert(_s); - - if (name) { - if (!unit_name_is_valid(name, UNIT_NAME_PLAIN)) - return sd_bus_error_setf(e, SD_BUS_ERROR_INVALID_ARGS, "Unit name %s is not valid.", name); - - if (!endswith(name, ".snapshot")) - return sd_bus_error_setf(e, SD_BUS_ERROR_INVALID_ARGS, "Unit name %s lacks snapshot suffix.", name); - - if (manager_get_unit(m, name)) - return sd_bus_error_setf(e, BUS_ERROR_UNIT_EXISTS, "Snapshot %s exists already.", name); - - } else { - - for (;;) { - if (asprintf(&n, "snapshot-%u.snapshot", ++ m->n_snapshots) < 0) - return -ENOMEM; - - if (!manager_get_unit(m, n)) { - name = n; - break; - } - - n = mfree(n); - } - } - - r = manager_load_unit_prepare(m, name, NULL, e, &u); - if (r < 0) - goto fail; - - u->transient = true; - manager_dispatch_load_queue(m); - assert(u->load_state == UNIT_LOADED); - - HASHMAP_FOREACH_KEY(other, k, m->units, i) { - - if (other->ignore_on_snapshot || - other->transient) - continue; - - if (k != other->id) - continue; - - if (UNIT_VTABLE(other)->check_snapshot) - if (!UNIT_VTABLE(other)->check_snapshot(other)) - continue; - - if (!UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other))) - continue; - - r = unit_add_two_dependencies(u, UNIT_AFTER, UNIT_WANTS, other, true); - if (r < 0) - goto fail; - } - - SNAPSHOT(u)->cleanup = cleanup; - *_s = SNAPSHOT(u); - - log_unit_info(u, "Created snapshot."); - - return 0; - -fail: - if (u) - unit_add_to_cleanup_queue(u); - - return r; -} - -void snapshot_remove(Snapshot *s) { - assert(s); - - log_unit_info(UNIT(s), "Removing snapshot."); - - unit_add_to_cleanup_queue(UNIT(s)); -} - -const UnitVTable snapshot_vtable = { - .object_size = sizeof(Snapshot), - - .no_alias = true, - .no_instances = true, - .no_gc = true, - - .init = snapshot_init, - .load = snapshot_load, - - .coldplug = snapshot_coldplug, - - .dump = snapshot_dump, - - .start = snapshot_start, - .stop = snapshot_stop, - - .serialize = snapshot_serialize, - .deserialize_item = snapshot_deserialize_item, - - .active_state = snapshot_active_state, - .sub_state_to_string = snapshot_sub_state_to_string, - - .bus_vtable = bus_snapshot_vtable -}; diff --git a/src/core/snapshot.h b/src/core/snapshot.h deleted file mode 100644 index bd52dea408..0000000000 --- a/src/core/snapshot.h +++ /dev/null @@ -1,39 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#pragma once - -/*** - This file is part of systemd. - - Copyright 2010 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/>. -***/ - -typedef struct Snapshot Snapshot; - -#include "unit.h" - -struct Snapshot { - Unit meta; - - SnapshotState state, deserialized_state; - - bool cleanup; -}; - -extern const UnitVTable snapshot_vtable; - -int snapshot_create(Manager *m, const char *name, bool cleanup, sd_bus_error *e, Snapshot **s); -void snapshot_remove(Snapshot *s); diff --git a/src/core/socket.c b/src/core/socket.c index 3c7f972fbc..687675b24e 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -296,6 +296,9 @@ static int socket_add_default_dependencies(Socket *s) { int r; assert(s); + if (!UNIT(s)->default_dependencies) + return 0; + r = unit_add_dependency_by_name(UNIT(s), UNIT_BEFORE, SPECIAL_SOCKETS_TARGET, NULL, true); if (r < 0) return r; @@ -365,11 +368,9 @@ static int socket_add_extras(Socket *s) { return r; } - if (u->default_dependencies) { - r = socket_add_default_dependencies(s); - if (r < 0) - return r; - } + r = socket_add_default_dependencies(s); + if (r < 0) + return r; return 0; } @@ -1265,6 +1266,19 @@ static int socket_open_fds(Socket *s) { know_label = true; } + /* Apply the socket protocol */ + switch(p->address.type) { + case SOCK_STREAM: + case SOCK_SEQPACKET: + if (p->socket->socket_protocol == IPPROTO_SCTP) + p->address.protocol = p->socket->socket_protocol; + break; + case SOCK_DGRAM: + if (p->socket->socket_protocol == IPPROTO_UDPLITE) + p->address.protocol = p->socket->socket_protocol; + break; + } + r = socket_address_listen( &p->address, SOCK_CLOEXEC|SOCK_NONBLOCK, @@ -1920,7 +1934,7 @@ static void socket_enter_running(Socket *s, int cfd) { goto fail; } - r = manager_add_job(UNIT(s)->manager, JOB_START, UNIT_DEREF(s->service), JOB_REPLACE, true, &error, NULL); + r = manager_add_job(UNIT(s)->manager, JOB_START, UNIT_DEREF(s->service), JOB_REPLACE, &error, NULL); if (r < 0) goto fail; } @@ -1978,7 +1992,7 @@ static void socket_enter_running(Socket *s, int cfd) { cfd = -1; s->n_connections ++; - r = manager_add_job(UNIT(s)->manager, JOB_START, UNIT(service), JOB_REPLACE, true, &error, NULL); + r = manager_add_job(UNIT(s)->manager, JOB_START, UNIT(service), JOB_REPLACE, &error, NULL); if (r < 0) goto fail; @@ -2332,7 +2346,7 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value, return 0; } -static int socket_distribute_fds(Unit *u, FDSet *fds) { +static void socket_distribute_fds(Unit *u, FDSet *fds) { Socket *s = SOCKET(u); SocketPort *p; @@ -2356,8 +2370,6 @@ static int socket_distribute_fds(Unit *u, FDSet *fds) { } } } - - return 0; } _pure_ static UnitActiveState socket_active_state(Unit *u) { diff --git a/src/core/socket.h b/src/core/socket.h index 94cda8a90d..08033287a6 100644 --- a/src/core/socket.h +++ b/src/core/socket.h @@ -23,9 +23,9 @@ typedef struct Socket Socket; -#include "socket-util.h" #include "mount.h" #include "service.h" +#include "socket-util.h" typedef enum SocketExecCommand { SOCKET_EXEC_START_PRE, @@ -120,6 +120,8 @@ struct Socket { bool remove_on_stop; bool writable; + int socket_protocol; + /* Socket options */ bool keep_alive; bool no_delay; diff --git a/src/core/swap.c b/src/core/swap.c index baaa27b6a3..b6e4372fc0 100644 --- a/src/core/swap.c +++ b/src/core/swap.c @@ -211,14 +211,25 @@ static int swap_add_device_links(Swap *s) { } static int swap_add_default_dependencies(Swap *s) { + int r; + assert(s); + if (!UNIT(s)->default_dependencies) + return 0; + if (UNIT(s)->manager->running_as != MANAGER_SYSTEM) return 0; if (detect_container() > 0) return 0; + /* swap units generated for the swap dev links are missing the + * ordering dep against the swap target. */ + r = unit_add_dependency_by_name(UNIT(s), UNIT_BEFORE, SPECIAL_SWAP_TARGET, NULL, true); + if (r < 0) + return r; + return unit_add_two_dependencies_by_name(UNIT(s), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, NULL, true); } @@ -331,11 +342,9 @@ static int swap_load(Unit *u) { if (r < 0) return r; - if (UNIT(s)->default_dependencies) { - r = swap_add_default_dependencies(s); - if (r < 0) - return r; - } + r = swap_add_default_dependencies(s); + if (r < 0) + return r; } return swap_verify(s); @@ -1268,26 +1277,36 @@ static void swap_shutdown(Manager *m) { m->swaps_by_devnode = hashmap_free(m->swaps_by_devnode); } -static int swap_enumerate(Manager *m) { +static void swap_enumerate(Manager *m) { int r; assert(m); if (!m->proc_swaps) { m->proc_swaps = fopen("/proc/swaps", "re"); - if (!m->proc_swaps) - return errno == ENOENT ? 0 : -errno; + if (!m->proc_swaps) { + if (errno == ENOENT) + log_debug("Not swap enabled, skipping enumeration"); + else + log_error_errno(errno, "Failed to open /proc/swaps: %m"); + + return; + } r = sd_event_add_io(m->event, &m->swap_event_source, fileno(m->proc_swaps), EPOLLPRI, swap_dispatch_io, m); - if (r < 0) + if (r < 0) { + log_error_errno(r, "Failed to watch /proc/swaps: %m"); goto fail; + } /* Dispatch this before we dispatch SIGCHLD, so that * we always get the events from /proc/swaps before * the SIGCHLD of /sbin/swapon. */ r = sd_event_source_set_priority(m->swap_event_source, -10); - if (r < 0) + if (r < 0) { + log_error_errno(r, "Failed to change /proc/swaps priority: %m"); goto fail; + } (void) sd_event_source_set_description(m->swap_event_source, "swap-proc"); } @@ -1296,11 +1315,10 @@ static int swap_enumerate(Manager *m) { if (r < 0) goto fail; - return 0; + return; fail: swap_shutdown(m); - return r; } int swap_process_device_new(Manager *m, struct udev_device *dev) { diff --git a/src/core/system.conf b/src/core/system.conf index 50668e12c4..e2ded27333 100644 --- a/src/core/system.conf +++ b/src/core/system.conf @@ -40,7 +40,8 @@ #DefaultCPUAccounting=no #DefaultBlockIOAccounting=no #DefaultMemoryAccounting=no -#DefaultTasksAccounting=no +#DefaultTasksAccounting=yes +#DefaultTasksMax=512 #DefaultLimitCPU= #DefaultLimitFSIZE= #DefaultLimitDATA= diff --git a/src/core/target.c b/src/core/target.c index c3e79fffc8..14f9b2e26a 100644 --- a/src/core/target.c +++ b/src/core/target.c @@ -52,9 +52,7 @@ static int target_add_default_dependencies(Target *t) { static const UnitDependency deps[] = { UNIT_REQUIRES, - UNIT_REQUIRES_OVERRIDABLE, UNIT_REQUISITE, - UNIT_REQUISITE_OVERRIDABLE, UNIT_WANTS, UNIT_BINDS_TO, UNIT_PART_OF diff --git a/src/core/timer.c b/src/core/timer.c index c9dc97d4fb..6b0f8e8616 100644 --- a/src/core/timer.c +++ b/src/core/timer.c @@ -27,6 +27,7 @@ #include "dbus-timer.h" #include "fs-util.h" #include "parse-util.h" +#include "random-util.h" #include "special.h" #include "string-table.h" #include "string-util.h" @@ -55,6 +56,7 @@ static void timer_init(Unit *u) { t->next_elapse_monotonic_or_boottime = USEC_INFINITY; t->next_elapse_realtime = USEC_INFINITY; t->accuracy_usec = u->manager->default_timer_accuracy_usec; + t->remain_after_elapse = true; } void timer_free_values(Timer *t) { @@ -102,6 +104,9 @@ static int timer_add_default_dependencies(Timer *t) { assert(t); + if (!UNIT(t)->default_dependencies) + return 0; + r = unit_add_dependency_by_name(UNIT(t), UNIT_BEFORE, SPECIAL_TIMERS_TARGET, NULL, true); if (r < 0) return r; @@ -192,11 +197,9 @@ static int timer_load(Unit *u) { if (r < 0) return r; - if (u->default_dependencies) { - r = timer_add_default_dependencies(t); - if (r < 0) - return r; - } + r = timer_add_default_dependencies(t); + if (r < 0) + return r; } return timer_verify(t); @@ -216,13 +219,15 @@ static void timer_dump(Unit *u, FILE *f, const char *prefix) { "%sUnit: %s\n" "%sPersistent: %s\n" "%sWakeSystem: %s\n" - "%sAccuracy: %s\n", + "%sAccuracy: %s\n" + "%sRemainAfterElapse: %s\n", prefix, timer_state_to_string(t->state), prefix, timer_result_to_string(t->result), prefix, trigger ? trigger->id : "n/a", prefix, yes_no(t->persistent), prefix, yes_no(t->wake_system), - prefix, format_timespan(buf, sizeof(buf), t->accuracy_usec, 1)); + prefix, format_timespan(buf, sizeof(buf), t->accuracy_usec, 1), + prefix, yes_no(t->remain_after_elapse)); LIST_FOREACH(value, v, t->values) { @@ -274,13 +279,13 @@ static int timer_coldplug(Unit *u) { assert(t); assert(t->state == TIMER_DEAD); - if (t->deserialized_state != t->state) { + if (t->deserialized_state == t->state) + return 0; - if (t->deserialized_state == TIMER_WAITING) - timer_enter_waiting(t, false); - else - timer_set_state(t, t->deserialized_state); - } + if (t->deserialized_state == TIMER_WAITING) + timer_enter_waiting(t, false); + else + timer_set_state(t, t->deserialized_state); return 0; } @@ -294,6 +299,23 @@ static void timer_enter_dead(Timer *t, TimerResult f) { timer_set_state(t, t->result != TIMER_SUCCESS ? TIMER_FAILED : TIMER_DEAD); } +static void timer_enter_elapsed(Timer *t, bool leave_around) { + assert(t); + + /* If a unit is marked with RemainAfterElapse=yes we leave it + * around even after it elapsed once, so that starting it + * later again does not necessarily mean immediate + * retriggering. We unconditionally leave units with + * TIMER_UNIT_ACTIVE or TIMER_UNIT_INACTIVE triggers around, + * since they might be restarted automatically at any time + * later on. */ + + if (t->remain_after_elapse || leave_around) + timer_set_state(t, TIMER_ELAPSED); + else + timer_enter_dead(t, TIMER_SUCCESS); +} + static usec_t monotonic_to_boottime(usec_t t) { usec_t a, b; @@ -309,10 +331,33 @@ static usec_t monotonic_to_boottime(usec_t t) { return 0; } +static void add_random(Timer *t, usec_t *v) { + char s[FORMAT_TIMESPAN_MAX]; + usec_t add; + + assert(t); + assert(*v); + + if (t->random_usec == 0) + return; + if (*v == USEC_INFINITY) + return; + + add = random_u64() % t->random_usec; + + if (*v + add < *v) /* overflow */ + *v = (usec_t) -2; /* Highest possible value, that is not USEC_INFINITY */ + else + *v += add; + + log_unit_info(UNIT(t), "Adding %s random time.", format_timespan(s, sizeof(s), add, 0)); +} + static void timer_enter_waiting(Timer *t, bool initial) { bool found_monotonic = false, found_realtime = false; usec_t ts_realtime, ts_monotonic; usec_t base = 0; + bool leave_around = false; TimerValue *v; int r; @@ -373,7 +418,7 @@ static void timer_enter_waiting(Timer *t, bool initial) { break; case TIMER_UNIT_ACTIVE: - + leave_around = true; base = UNIT_TRIGGER(UNIT(t))->inactive_exit_timestamp.monotonic; if (base <= 0) @@ -385,7 +430,7 @@ static void timer_enter_waiting(Timer *t, bool initial) { break; case TIMER_UNIT_INACTIVE: - + leave_around = true; base = UNIT_TRIGGER(UNIT(t))->inactive_enter_timestamp.monotonic; if (base <= 0) @@ -422,14 +467,18 @@ static void timer_enter_waiting(Timer *t, bool initial) { if (!found_monotonic && !found_realtime) { log_unit_debug(UNIT(t), "Timer is elapsed."); - timer_set_state(t, TIMER_ELAPSED); + timer_enter_elapsed(t, leave_around); return; } if (found_monotonic) { char buf[FORMAT_TIMESPAN_MAX]; + usec_t left; + + add_random(t, &t->next_elapse_monotonic_or_boottime); - log_unit_debug(UNIT(t), "Monotonic timer elapses in %s.", format_timespan(buf, sizeof(buf), t->next_elapse_monotonic_or_boottime > ts_monotonic ? t->next_elapse_monotonic_or_boottime - ts_monotonic : 0, 0)); + left = t->next_elapse_monotonic_or_boottime > ts_monotonic ? t->next_elapse_monotonic_or_boottime - ts_monotonic : 0; + log_unit_debug(UNIT(t), "Monotonic timer elapses in %s.", format_timespan(buf, sizeof(buf), left, 0)); if (t->monotonic_event_source) { r = sd_event_source_set_time(t->monotonic_event_source, t->next_elapse_monotonic_or_boottime); @@ -462,6 +511,9 @@ static void timer_enter_waiting(Timer *t, bool initial) { if (found_realtime) { char buf[FORMAT_TIMESTAMP_MAX]; + + add_random(t, &t->next_elapse_realtime); + log_unit_debug(UNIT(t), "Realtime timer elapses at %s.", format_timestamp(buf, sizeof(buf), t->next_elapse_realtime)); if (t->realtime_event_source) { @@ -510,15 +562,14 @@ static void timer_enter_running(Timer *t) { if (unit_stop_pending(UNIT(t))) return; - r = manager_add_job(UNIT(t)->manager, JOB_START, UNIT_TRIGGER(UNIT(t)), - JOB_REPLACE, true, &error, NULL); + r = manager_add_job(UNIT(t)->manager, JOB_START, UNIT_TRIGGER(UNIT(t)), JOB_REPLACE, &error, NULL); if (r < 0) goto fail; dual_timestamp_get(&t->last_trigger); if (t->stamp_path) - touch_file(t->stamp_path, true, t->last_trigger.realtime, UID_INVALID, GID_INVALID, 0); + touch_file(t->stamp_path, true, t->last_trigger.realtime, UID_INVALID, GID_INVALID, MODE_INVALID); timer_set_state(t, TIMER_RUNNING); return; @@ -554,7 +605,7 @@ static int timer_start(Unit *u) { /* The timer has never run before, * make sure a stamp file exists. */ - touch_file(t->stamp_path, true, USEC_INFINITY, UID_INVALID, GID_INVALID, 0); + touch_file(t->stamp_path, true, USEC_INFINITY, UID_INVALID, GID_INVALID, MODE_INVALID); } t->result = TIMER_SUCCESS; diff --git a/src/core/timer.h b/src/core/timer.h index ac5af6a93c..0599f07818 100644 --- a/src/core/timer.h +++ b/src/core/timer.h @@ -58,6 +58,7 @@ struct Timer { Unit meta; usec_t accuracy_usec; + usec_t random_usec; LIST_HEAD(TimerValue, values); usec_t next_elapse_realtime; @@ -73,6 +74,7 @@ struct Timer { bool persistent; bool wake_system; + bool remain_after_elapse; char *stamp_path; }; diff --git a/src/core/transaction.c b/src/core/transaction.c index 69f28c902f..15e79d00b3 100644 --- a/src/core/transaction.c +++ b/src/core/transaction.c @@ -19,8 +19,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <unistd.h> #include <fcntl.h> +#include <unistd.h> #include "alloc-util.h" #include "bus-common-errors.h" @@ -99,9 +99,7 @@ static void transaction_merge_and_delete_job(Transaction *tr, Job *j, Job *other j->type = t; j->state = JOB_WAITING; - j->override = j->override || other->override; j->irreversible = j->irreversible || other->irreversible; - j->matters_to_anchor = j->matters_to_anchor || other->matters_to_anchor; /* Patch us in as new owner of the JobDependency objects */ @@ -745,7 +743,7 @@ int transaction_activate(Transaction *tr, Manager *m, JobMode mode, sd_bus_error return 0; } -static Job* transaction_add_one_job(Transaction *tr, JobType type, Unit *unit, bool override, bool *is_new) { +static Job* transaction_add_one_job(Transaction *tr, JobType type, Unit *unit, bool *is_new) { Job *j, *f; assert(tr); @@ -774,7 +772,6 @@ static Job* transaction_add_one_job(Transaction *tr, JobType type, Unit *unit, b j->generation = 0; j->marker = NULL; j->matters_to_anchor = false; - j->override = override; j->irreversible = tr->irreversible; LIST_PREPEND(transaction, f, j); @@ -833,7 +830,6 @@ int transaction_add_job_and_dependencies( Unit *unit, Job *by, bool matters, - bool override, bool conflicts, bool ignore_requirements, bool ignore_order, @@ -895,7 +891,7 @@ int transaction_add_job_and_dependencies( /* First add the job. */ - ret = transaction_add_one_job(tr, type, unit, override, &is_new); + ret = transaction_add_one_job(tr, type, unit, &is_new); if (!ret) return -ENOMEM; @@ -918,7 +914,7 @@ int transaction_add_job_and_dependencies( * add all dependencies of everybody following. */ if (unit_following_set(ret->unit, &following) > 0) { SET_FOREACH(dep, following, i) { - r = transaction_add_job_and_dependencies(tr, type, dep, ret, false, override, false, false, ignore_order, e); + r = transaction_add_job_and_dependencies(tr, type, dep, ret, false, false, false, ignore_order, e); if (r < 0) { log_unit_warning(dep, "Cannot add dependency job for, ignoring: %s", bus_error_message(e, r)); sd_bus_error_free(e); @@ -931,7 +927,7 @@ int transaction_add_job_and_dependencies( /* Finally, recursively add in all dependencies. */ if (type == JOB_START || type == JOB_RESTART) { SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUIRES], i) { - r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, true, override, false, false, ignore_order, e); + r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, true, false, false, ignore_order, e); if (r < 0) { if (r != -EBADR) goto fail; @@ -941,7 +937,7 @@ int transaction_add_job_and_dependencies( } SET_FOREACH(dep, ret->unit->dependencies[UNIT_BINDS_TO], i) { - r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, true, override, false, false, ignore_order, e); + r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, true, false, false, ignore_order, e); if (r < 0) { if (r != -EBADR) goto fail; @@ -950,19 +946,8 @@ int transaction_add_job_and_dependencies( } } - SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUIRES_OVERRIDABLE], i) { - r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, !override, override, false, false, ignore_order, e); - if (r < 0) { - log_unit_full(dep, - r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_WARNING, r, - "Cannot add dependency job, ignoring: %s", - bus_error_message(e, r)); - sd_bus_error_free(e); - } - } - SET_FOREACH(dep, ret->unit->dependencies[UNIT_WANTS], i) { - r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, false, false, false, false, ignore_order, e); + r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, false, false, false, ignore_order, e); if (r < 0) { log_unit_full(dep, r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_WARNING, r, @@ -973,7 +958,7 @@ int transaction_add_job_and_dependencies( } SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUISITE], i) { - r = transaction_add_job_and_dependencies(tr, JOB_VERIFY_ACTIVE, dep, ret, true, override, false, false, ignore_order, e); + r = transaction_add_job_and_dependencies(tr, JOB_VERIFY_ACTIVE, dep, ret, true, false, false, ignore_order, e); if (r < 0) { if (r != -EBADR) goto fail; @@ -982,19 +967,8 @@ int transaction_add_job_and_dependencies( } } - SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUISITE_OVERRIDABLE], i) { - r = transaction_add_job_and_dependencies(tr, JOB_VERIFY_ACTIVE, dep, ret, !override, override, false, false, ignore_order, e); - if (r < 0) { - log_unit_full(dep, - r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_WARNING, r, - "Cannot add dependency job, ignoring: %s", - bus_error_message(e, r)); - sd_bus_error_free(e); - } - } - SET_FOREACH(dep, ret->unit->dependencies[UNIT_CONFLICTS], i) { - r = transaction_add_job_and_dependencies(tr, JOB_STOP, dep, ret, true, override, true, false, ignore_order, e); + r = transaction_add_job_and_dependencies(tr, JOB_STOP, dep, ret, true, true, false, ignore_order, e); if (r < 0) { if (r != -EBADR) goto fail; @@ -1004,7 +978,7 @@ int transaction_add_job_and_dependencies( } SET_FOREACH(dep, ret->unit->dependencies[UNIT_CONFLICTED_BY], i) { - r = transaction_add_job_and_dependencies(tr, JOB_STOP, dep, ret, false, override, false, false, ignore_order, e); + r = transaction_add_job_and_dependencies(tr, JOB_STOP, dep, ret, false, false, false, ignore_order, e); if (r < 0) { log_unit_warning(dep, "Cannot add dependency job, ignoring: %s", @@ -1039,7 +1013,7 @@ int transaction_add_job_and_dependencies( if (nt == JOB_NOP) continue; - r = transaction_add_job_and_dependencies(tr, nt, dep, ret, true, override, false, false, ignore_order, e); + r = transaction_add_job_and_dependencies(tr, nt, dep, ret, true, false, false, ignore_order, e); if (r < 0) { if (r != -EBADR) goto fail; @@ -1052,7 +1026,7 @@ int transaction_add_job_and_dependencies( if (type == JOB_RELOAD) { SET_FOREACH(dep, ret->unit->dependencies[UNIT_PROPAGATES_RELOAD_TO], i) { - r = transaction_add_job_and_dependencies(tr, JOB_RELOAD, dep, ret, false, override, false, false, ignore_order, e); + r = transaction_add_job_and_dependencies(tr, JOB_RELOAD, dep, ret, false, false, false, ignore_order, e); if (r < 0) { log_unit_warning(dep, "Cannot add dependency reload job, ignoring: %s", @@ -1097,7 +1071,7 @@ int transaction_add_isolate_jobs(Transaction *tr, Manager *m) { if (hashmap_get(tr->jobs, u)) continue; - r = transaction_add_job_and_dependencies(tr, JOB_STOP, u, tr->anchor_job, true, false, false, false, false, NULL); + r = transaction_add_job_and_dependencies(tr, JOB_STOP, u, tr->anchor_job, true, false, false, false, NULL); if (r < 0) log_unit_warning_errno(u, r, "Cannot add isolate job, ignoring: %m"); } diff --git a/src/core/transaction.h b/src/core/transaction.h index d949b21b8d..5c4a13edab 100644 --- a/src/core/transaction.h +++ b/src/core/transaction.h @@ -23,10 +23,10 @@ typedef struct Transaction Transaction; -#include "unit.h" -#include "manager.h" -#include "job.h" #include "hashmap.h" +#include "job.h" +#include "manager.h" +#include "unit.h" struct Transaction { /* Jobs to be added */ @@ -44,7 +44,6 @@ int transaction_add_job_and_dependencies( Unit *unit, Job *by, bool matters, - bool override, bool conflicts, bool ignore_requirements, bool ignore_order, diff --git a/src/core/unit-printf.c b/src/core/unit-printf.c index 721c8ccce9..f587a5a141 100644 --- a/src/core/unit-printf.c +++ b/src/core/unit-printf.c @@ -159,162 +159,43 @@ static int specifier_runtime(char specifier, void *data, void *userdata, char ** } static int specifier_user_name(char specifier, void *data, void *userdata, char **ret) { - char *printed = NULL; - Unit *u = userdata; - ExecContext *c; - int r = 0; - - assert(u); - - c = unit_get_exec_context(u); - if (!c) - return -EOPNOTSUPP; - - if (u->manager->running_as == MANAGER_SYSTEM) { - - /* We cannot use NSS from PID 1, hence try to make the - * best of it in that case, and fail if we can't help - * it */ - - if (!c->user || streq(c->user, "root") || streq(c->user, "0")) - printed = strdup(specifier == 'u' ? "root" : "0"); - else { - if (specifier == 'u') - printed = strdup(c->user); - else { - uid_t uid; + char *t; - r = parse_uid(c->user, &uid); - if (r < 0) - return -ENODATA; - - r = asprintf(&printed, UID_FMT, uid); - } - } - - } else { - _cleanup_free_ char *tmp = NULL; - const char *username = NULL; - uid_t uid; - - if (c->user) - username = c->user; - else - /* get USER env from env or our own uid */ - username = tmp = getusername_malloc(); - - /* fish username from passwd */ - r = get_user_creds(&username, &uid, NULL, NULL, NULL); - if (r < 0) - return r; - - if (specifier == 'u') - printed = strdup(username); - else - r = asprintf(&printed, UID_FMT, uid); - } + /* If we are UID 0 (root), this will not result in NSS, + * otherwise it might. This is good, as we want to be able to + * run this in PID 1, where our user ID is 0, but where NSS + * lookups are not allowed. */ - if (r < 0 || !printed) + t = getusername_malloc(); + if (!t) return -ENOMEM; - *ret = printed; + *ret = t; return 0; } -static int specifier_user_home(char specifier, void *data, void *userdata, char **ret) { - Unit *u = userdata; - ExecContext *c; - char *n; - int r; - - assert(u); - - c = unit_get_exec_context(u); - if (!c) - return -EOPNOTSUPP; - - if (u->manager->running_as == MANAGER_SYSTEM) { - - /* We cannot use NSS from PID 1, hence try to make the - * best of it if we can, but fail if we can't */ - - if (!c->user || streq(c->user, "root") || streq(c->user, "0")) - n = strdup("/root"); - else - return -EOPNOTSUPP; - - } else { +static int specifier_user_id(char specifier, void *data, void *userdata, char **ret) { - /* return HOME if set, otherwise from passwd */ - if (!c || !c->user) { - r = get_home_dir(&n); - if (r < 0) - return r; - } else { - const char *username, *home; - - username = c->user; - r = get_user_creds(&username, NULL, NULL, &home, NULL); - if (r < 0) - return r; - - n = strdup(home); - } - } - - if (!n) + if (asprintf(ret, UID_FMT, getuid()) < 0) return -ENOMEM; - *ret = n; return 0; } -static int specifier_user_shell(char specifier, void *data, void *userdata, char **ret) { - Unit *u = userdata; - ExecContext *c; - char *n; - int r; - - assert(u); - - c = unit_get_exec_context(u); - if (!c) - return -EOPNOTSUPP; - - if (u->manager->running_as == MANAGER_SYSTEM) { - - /* We cannot use NSS from PID 1, hence try to make the - * best of it if we can, but fail if we can't */ - - if (!c->user || streq(c->user, "root") || streq(c->user, "0")) - n = strdup("/bin/sh"); - else - return -EOPNOTSUPP; - - } else { +static int specifier_user_home(char specifier, void *data, void *userdata, char **ret) { - /* return /bin/sh for root, otherwise the value from passwd */ - if (!c->user) { - r = get_shell(&n); - if (r < 0) - return r; - } else { - const char *username, *shell; + /* On PID 1 (which runs as root) this will not result in NSS, + * which is good. See above */ - username = c->user; - r = get_user_creds(&username, NULL, NULL, NULL, &shell); - if (r < 0) - return r; + return get_home_dir(ret); +} - n = strdup(shell); - } - } +static int specifier_user_shell(char specifier, void *data, void *userdata, char **ret) { - if (!n) - return -ENOMEM; + /* On PID 1 (which runs as root) this will not result in NSS, + * which is good. See above */ - *ret = n; - return 0; + return get_shell(ret); } int unit_name_printf(Unit *u, const char* format, char **ret) { @@ -354,10 +235,10 @@ int unit_full_printf(Unit *u, const char *format, char **ret) { * %r where units in this slice are placed in the cgroup tree * %R the root of this systemd's instance tree * %t the runtime directory to place sockets in (e.g. "/run" or $XDG_RUNTIME_DIR) - * %U the UID of the configured user or running user - * %u the username of the configured user or running user - * %h the homedir of the configured user or running user - * %s the shell of the configured user or running user + * %U the UID of the running user + * %u the username of the running user + * %h the homedir of the running user + * %s the shell of the running user * %m the machine ID of the running system * %H the host name of the running system * %b the boot ID of the running system @@ -377,7 +258,8 @@ int unit_full_printf(Unit *u, const char *format, char **ret) { { 'r', specifier_cgroup_slice, NULL }, { 'R', specifier_cgroup_root, NULL }, { 't', specifier_runtime, NULL }, - { 'U', specifier_user_name, NULL }, + + { 'U', specifier_user_id, NULL }, { 'u', specifier_user_name, NULL }, { 'h', specifier_user_home, NULL }, { 's', specifier_user_shell, NULL }, diff --git a/src/core/unit.c b/src/core/unit.c index 6c130d4cd1..0a02e38aa8 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -63,7 +63,6 @@ const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX] = { [UNIT_SOCKET] = &socket_vtable, [UNIT_BUSNAME] = &busname_vtable, [UNIT_TARGET] = &target_vtable, - [UNIT_SNAPSHOT] = &snapshot_vtable, [UNIT_DEVICE] = &device_vtable, [UNIT_MOUNT] = &mount_vtable, [UNIT_AUTOMOUNT] = &automount_vtable, @@ -133,6 +132,9 @@ static void unit_init(Unit *u) { cc->blockio_accounting = u->manager->default_blockio_accounting; cc->memory_accounting = u->manager->default_memory_accounting; cc->tasks_accounting = u->manager->default_tasks_accounting; + + if (u->type != UNIT_SLICE) + cc->tasks_max = u->manager->default_tasks_max; } ec = unit_get_exec_context(u); @@ -315,9 +317,6 @@ bool unit_check_gc(Unit *u) { if (state != UNIT_INACTIVE) return true; - if (UNIT_VTABLE(u)->no_gc) - return true; - if (u->no_gc) return true; @@ -998,15 +997,13 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) { "%s\tRefuseManualStop: %s\n" "%s\tDefaultDependencies: %s\n" "%s\tOnFailureJobMode: %s\n" - "%s\tIgnoreOnIsolate: %s\n" - "%s\tIgnoreOnSnapshot: %s\n", + "%s\tIgnoreOnIsolate: %s\n", prefix, yes_no(u->stop_when_unneeded), prefix, yes_no(u->refuse_manual_start), prefix, yes_no(u->refuse_manual_stop), prefix, yes_no(u->default_dependencies), prefix, job_mode_to_string(u->on_failure_job_mode), - prefix, yes_no(u->ignore_on_isolate), - prefix, yes_no(u->ignore_on_snapshot)); + prefix, yes_no(u->ignore_on_isolate)); if (UNIT_VTABLE(u)->dump) UNIT_VTABLE(u)->dump(u, f, prefix2); @@ -1104,9 +1101,7 @@ static int unit_add_target_dependencies(Unit *u) { static const UnitDependency deps[] = { UNIT_REQUIRED_BY, - UNIT_REQUIRED_BY_OVERRIDABLE, UNIT_REQUISITE_OF, - UNIT_REQUISITE_OF_OVERRIDABLE, UNIT_WANTED_BY, UNIT_BOUND_BY }; @@ -1352,12 +1347,18 @@ static bool unit_assert_test(Unit *u) { return u->assert_result; } +void unit_status_printf(Unit *u, const char *status, const char *unit_status_msg_format) { + DISABLE_WARNING_FORMAT_NONLITERAL; + manager_status_printf(u->manager, STATUS_TYPE_NORMAL, status, unit_status_msg_format, unit_description(u)); + REENABLE_WARNING; +} + _pure_ static const char* unit_get_status_message_format(Unit *u, JobType t) { const char *format; const UnitStatusMessageFormats *format_table; assert(u); - assert(t == JOB_START || t == JOB_STOP || t == JOB_RELOAD); + assert(IN_SET(t, JOB_START, JOB_STOP, JOB_RELOAD)); if (t != JOB_RELOAD) { format_table = &UNIT_VTABLE(u)->status_message_formats; @@ -1382,6 +1383,10 @@ static void unit_status_print_starting_stopping(Unit *u, JobType t) { assert(u); + /* Reload status messages have traditionally not been printed to console. */ + if (!IN_SET(t, JOB_START, JOB_STOP)) + return; + format = unit_get_status_message_format(u, t); DISABLE_WARNING_FORMAT_NONLITERAL; @@ -1396,7 +1401,7 @@ static void unit_status_log_starting_stopping_reloading(Unit *u, JobType t) { assert(u); - if (t != JOB_START && t != JOB_STOP && t != JOB_RELOAD) + if (!IN_SET(t, JOB_START, JOB_STOP, JOB_RELOAD)) return; if (log_on_console()) @@ -1428,12 +1433,12 @@ static void unit_status_log_starting_stopping_reloading(Unit *u, JobType t) { } void unit_status_emit_starting_stopping_reloading(Unit *u, JobType t) { + assert(u); + assert(t >= 0); + assert(t < _JOB_TYPE_MAX); unit_status_log_starting_stopping_reloading(u, t); - - /* Reload status messages have traditionally not been printed to console. */ - if (t != JOB_RELOAD) - unit_status_print_starting_stopping(u, t); + unit_status_print_starting_stopping(u, t); } /* Errors: @@ -1608,11 +1613,11 @@ bool unit_can_reload(Unit *u) { static void unit_check_unneeded(Unit *u) { + _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; + static const UnitDependency needed_dependencies[] = { UNIT_REQUIRED_BY, - UNIT_REQUIRED_BY_OVERRIDABLE, UNIT_REQUISITE_OF, - UNIT_REQUISITE_OF_OVERRIDABLE, UNIT_WANTED_BY, UNIT_BOUND_BY, }; @@ -1649,12 +1654,13 @@ static void unit_check_unneeded(Unit *u) { log_unit_info(u, "Unit not needed anymore. Stopping."); /* Ok, nobody needs us anymore. Sniff. Then let's commit suicide */ - r = manager_add_job(u->manager, JOB_STOP, u, JOB_FAIL, true, NULL, NULL); + r = manager_add_job(u->manager, JOB_STOP, u, JOB_FAIL, &error, NULL); if (r < 0) - log_unit_warning_errno(u, r, "Failed to enqueue stop job, ignoring: %m"); + log_unit_warning_errno(u, r, "Failed to enqueue stop job, ignoring: %s", bus_error_message(&error, r)); } static void unit_check_binds_to(Unit *u) { + _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; bool stop = false; Unit *other; Iterator i; @@ -1694,9 +1700,9 @@ static void unit_check_binds_to(Unit *u) { log_unit_info(u, "Unit is bound to inactive unit %s. Stopping, too.", other->id); /* A unit we need to run is gone. Sniff. Let's stop this. */ - r = manager_add_job(u->manager, JOB_STOP, u, JOB_FAIL, true, NULL, NULL); + r = manager_add_job(u->manager, JOB_STOP, u, JOB_FAIL, &error, NULL); if (r < 0) - log_unit_warning_errno(u, r, "Failed to enqueue stop job, ignoring: %m"); + log_unit_warning_errno(u, r, "Failed to enqueue stop job, ignoring: %s", bus_error_message(&error, r)); } static void retroactively_start_dependencies(Unit *u) { @@ -1709,30 +1715,25 @@ static void retroactively_start_dependencies(Unit *u) { SET_FOREACH(other, u->dependencies[UNIT_REQUIRES], i) if (!set_get(u->dependencies[UNIT_AFTER], other) && !UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other))) - manager_add_job(u->manager, JOB_START, other, JOB_REPLACE, true, NULL, NULL); + manager_add_job(u->manager, JOB_START, other, JOB_REPLACE, NULL, NULL); SET_FOREACH(other, u->dependencies[UNIT_BINDS_TO], i) if (!set_get(u->dependencies[UNIT_AFTER], other) && !UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other))) - manager_add_job(u->manager, JOB_START, other, JOB_REPLACE, true, NULL, NULL); - - SET_FOREACH(other, u->dependencies[UNIT_REQUIRES_OVERRIDABLE], i) - if (!set_get(u->dependencies[UNIT_AFTER], other) && - !UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other))) - manager_add_job(u->manager, JOB_START, other, JOB_FAIL, false, NULL, NULL); + manager_add_job(u->manager, JOB_START, other, JOB_REPLACE, NULL, NULL); SET_FOREACH(other, u->dependencies[UNIT_WANTS], i) if (!set_get(u->dependencies[UNIT_AFTER], other) && !UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other))) - manager_add_job(u->manager, JOB_START, other, JOB_FAIL, false, NULL, NULL); + manager_add_job(u->manager, JOB_START, other, JOB_FAIL, NULL, NULL); SET_FOREACH(other, u->dependencies[UNIT_CONFLICTS], i) if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other))) - manager_add_job(u->manager, JOB_STOP, other, JOB_REPLACE, true, NULL, NULL); + manager_add_job(u->manager, JOB_STOP, other, JOB_REPLACE, NULL, NULL); SET_FOREACH(other, u->dependencies[UNIT_CONFLICTED_BY], i) if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other))) - manager_add_job(u->manager, JOB_STOP, other, JOB_REPLACE, true, NULL, NULL); + manager_add_job(u->manager, JOB_STOP, other, JOB_REPLACE, NULL, NULL); } static void retroactively_stop_dependencies(Unit *u) { @@ -1745,7 +1746,7 @@ static void retroactively_stop_dependencies(Unit *u) { /* Pull down units which are bound to us recursively if enabled */ SET_FOREACH(other, u->dependencies[UNIT_BOUND_BY], i) if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other))) - manager_add_job(u->manager, JOB_STOP, other, JOB_REPLACE, true, NULL, NULL); + manager_add_job(u->manager, JOB_STOP, other, JOB_REPLACE, NULL, NULL); } static void check_unneeded_dependencies(Unit *u) { @@ -1759,18 +1760,12 @@ static void check_unneeded_dependencies(Unit *u) { SET_FOREACH(other, u->dependencies[UNIT_REQUIRES], i) if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other))) unit_check_unneeded(other); - SET_FOREACH(other, u->dependencies[UNIT_REQUIRES_OVERRIDABLE], i) - if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other))) - unit_check_unneeded(other); SET_FOREACH(other, u->dependencies[UNIT_WANTS], i) if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other))) unit_check_unneeded(other); SET_FOREACH(other, u->dependencies[UNIT_REQUISITE], i) if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other))) unit_check_unneeded(other); - SET_FOREACH(other, u->dependencies[UNIT_REQUISITE_OVERRIDABLE], i) - if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other))) - unit_check_unneeded(other); SET_FOREACH(other, u->dependencies[UNIT_BINDS_TO], i) if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other))) unit_check_unneeded(other); @@ -1790,7 +1785,7 @@ void unit_start_on_failure(Unit *u) { SET_FOREACH(other, u->dependencies[UNIT_ON_FAILURE], i) { int r; - r = manager_add_job(u->manager, JOB_START, other, u->on_failure_job_mode, true, NULL, NULL); + r = manager_add_job(u->manager, JOB_START, other, u->on_failure_job_mode, NULL, NULL); if (r < 0) log_unit_error_errno(u, r, "Failed to enqueue OnFailure= job: %m"); } @@ -2138,16 +2133,12 @@ int unit_add_dependency(Unit *u, UnitDependency d, Unit *other, bool add_referen static const UnitDependency inverse_table[_UNIT_DEPENDENCY_MAX] = { [UNIT_REQUIRES] = UNIT_REQUIRED_BY, - [UNIT_REQUIRES_OVERRIDABLE] = UNIT_REQUIRED_BY_OVERRIDABLE, [UNIT_WANTS] = UNIT_WANTED_BY, [UNIT_REQUISITE] = UNIT_REQUISITE_OF, - [UNIT_REQUISITE_OVERRIDABLE] = UNIT_REQUISITE_OF_OVERRIDABLE, [UNIT_BINDS_TO] = UNIT_BOUND_BY, [UNIT_PART_OF] = UNIT_CONSISTS_OF, [UNIT_REQUIRED_BY] = UNIT_REQUIRES, - [UNIT_REQUIRED_BY_OVERRIDABLE] = UNIT_REQUIRES_OVERRIDABLE, [UNIT_REQUISITE_OF] = UNIT_REQUISITE, - [UNIT_REQUISITE_OF_OVERRIDABLE] = UNIT_REQUISITE_OVERRIDABLE, [UNIT_WANTED_BY] = UNIT_WANTS, [UNIT_BOUND_BY] = UNIT_BINDS_TO, [UNIT_CONSISTS_OF] = UNIT_PART_OF, @@ -2890,7 +2881,7 @@ int unit_add_node_link(Unit *u, const char *what, bool wants) { } int unit_coldplug(Unit *u) { - int r; + int r = 0, q = 0; assert(u); @@ -2901,26 +2892,18 @@ int unit_coldplug(Unit *u) { u->coldplugged = true; - if (UNIT_VTABLE(u)->coldplug) { + if (UNIT_VTABLE(u)->coldplug) r = UNIT_VTABLE(u)->coldplug(u); - if (r < 0) - return r; - } - if (u->job) { - r = job_coldplug(u->job); - if (r < 0) - return r; - } + if (u->job) + q = job_coldplug(u->job); - return 0; -} + if (r < 0) + return r; + if (q < 0) + return q; -void unit_status_printf(Unit *u, const char *status, const char *unit_status_msg_format) { - DISABLE_WARNING_FORMAT_NONLITERAL; - manager_status_printf(u->manager, STATUS_TYPE_NORMAL, - status, unit_status_msg_format, unit_description(u)); - REENABLE_WARNING; + return 0; } bool unit_need_daemon_reload(Unit *u) { @@ -3152,12 +3135,19 @@ int unit_following_set(Unit *u, Set **s) { } UnitFileState unit_get_unit_file_state(Unit *u) { + int r; + assert(u); - if (u->unit_file_state < 0 && u->fragment_path) - u->unit_file_state = unit_file_get_state( + 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, - NULL, basename(u->fragment_path)); + NULL, + basename(u->fragment_path), + &u->unit_file_state); + if (r < 0) + u->unit_file_state = UNIT_FILE_BAD; + } return u->unit_file_state; } @@ -3168,7 +3158,8 @@ 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, - NULL, basename(u->fragment_path)); + NULL, + basename(u->fragment_path)); return u->unit_file_preset; } @@ -3440,7 +3431,15 @@ int unit_make_transient(Unit *u) { u->load_state = UNIT_STUB; u->load_error = 0; u->transient = true; + 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; + + unit_add_to_dbus_queue(u); + unit_add_to_gc_queue(u); + unit_add_to_load_queue(u); return 0; } @@ -3716,3 +3715,21 @@ int unit_fail_if_symlink(Unit *u, const char* where) { return -ELOOP; } + +bool unit_is_pristine(Unit *u) { + assert(u); + + /* Check if the unit already exists or is already around, + * in a number of different ways. Note that to cater for unit + * types such as slice, we are generally fine with units that + * are marked UNIT_LOADED even even though nothing was + * actually loaded, as those unit types don't require a file + * on disk to validly load. */ + + return !(!IN_SET(u->load_state, UNIT_NOT_FOUND, UNIT_LOADED) || + u->fragment_path || + u->source_path || + !strv_isempty(u->dropin_paths) || + u->job || + u->merged_into); +} diff --git a/src/core/unit.h b/src/core/unit.h index 6f775c5ce1..1681bbf53b 100644 --- a/src/core/unit.h +++ b/src/core/unit.h @@ -30,11 +30,11 @@ typedef struct UnitVTable UnitVTable; typedef struct UnitRef UnitRef; typedef struct UnitStatusMessageFormats UnitStatusMessageFormats; -#include "list.h" #include "condition.h" +#include "failure-action.h" #include "install.h" +#include "list.h" #include "unit-name.h" -#include "failure-action.h" typedef enum KillOperation { KILL_TERMINATE, @@ -203,9 +203,6 @@ struct Unit { /* Ignore this unit when isolating */ bool ignore_on_isolate; - /* Ignore this unit when snapshotting */ - bool ignore_on_snapshot; - /* Did the last condition check succeed? */ bool condition_result; bool assert_result; @@ -245,17 +242,16 @@ typedef enum UnitSetPropertiesMode { UNIT_PERSISTENT = 2, } UnitSetPropertiesMode; -#include "socket.h" +#include "automount.h" #include "busname.h" -#include "target.h" -#include "snapshot.h" #include "device.h" -#include "automount.h" -#include "swap.h" -#include "timer.h" -#include "slice.h" #include "path.h" #include "scope.h" +#include "slice.h" +#include "socket.h" +#include "swap.h" +#include "target.h" +#include "timer.h" struct UnitVTable { /* How much memory does an object of this unit type need */ @@ -322,7 +318,7 @@ struct UnitVTable { int (*deserialize_item)(Unit *u, const char *key, const char *data, FDSet *fds); /* Try to match up fds with what we need for this unit */ - int (*distribute_fds)(Unit *u, FDSet *fds); + void (*distribute_fds)(Unit *u, FDSet *fds); /* Boils down the more complex internal state of this unit to * a simpler one that the engine can understand */ @@ -343,9 +339,6 @@ struct UnitVTable { * shall release its runtime resources */ void (*release_resources)(Unit *u); - /* Return true when this unit is suitable for snapshotting */ - bool (*check_snapshot)(Unit *u); - /* Invoked on every child that died */ void (*sigchld_event)(Unit *u, pid_t pid, int code, int status); @@ -389,7 +382,7 @@ struct UnitVTable { * everything that is loaded here should still stay in * inactive state. It is the job of the coldplug() call above * to put the units into the initial state. */ - int (*enumerate)(Manager *m); + void (*enumerate)(Manager *m); /* Type specific cleanups. */ void (*shutdown)(Manager *m); @@ -410,9 +403,6 @@ struct UnitVTable { /* Instances make no sense for this type */ bool no_instances:1; - /* Exclude from automatic gc */ - bool no_gc:1; - /* True if transient units of this type are OK */ bool can_transient:1; }; @@ -443,7 +433,6 @@ DEFINE_CAST(SERVICE, Service); DEFINE_CAST(SOCKET, Socket); DEFINE_CAST(BUSNAME, BusName); DEFINE_CAST(TARGET, Target); -DEFINE_CAST(SNAPSHOT, Snapshot); DEFINE_CAST(DEVICE, Device); DEFINE_CAST(MOUNT, Mount); DEFINE_CAST(AUTOMOUNT, Automount); @@ -597,6 +586,8 @@ int unit_require_mounts_for(Unit *u, const char *path); bool unit_type_supported(UnitType t); +bool unit_is_pristine(Unit *u); + static inline bool unit_supported(Unit *u) { return unit_type_supported(u->type); } diff --git a/src/firstboot/firstboot.c b/src/firstboot/firstboot.c index 642d36912c..469ee7af68 100644 --- a/src/firstboot/firstboot.c +++ b/src/firstboot/firstboot.c @@ -542,7 +542,7 @@ static int process_root_password(void) { lock = take_etc_passwd_lock(arg_root); if (lock < 0) - return lock; + return log_error_errno(lock, "Failed to take a lock: %m"); if (arg_copy_root_password && arg_root) { struct spwd *p; diff --git a/src/fsck/fsck.c b/src/fsck/fsck.c index 5b806a1e69..e0436d794c 100644 --- a/src/fsck/fsck.c +++ b/src/fsck/fsck.c @@ -20,14 +20,14 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <stdio.h> -#include <stdbool.h> #include <errno.h> -#include <unistd.h> #include <fcntl.h> +#include <stdbool.h> +#include <stdio.h> #include <sys/file.h> -#include <sys/stat.h> #include <sys/prctl.h> +#include <sys/stat.h> +#include <unistd.h> #include "sd-bus.h" #include "sd-device.h" diff --git a/src/fstab-generator/fstab-generator.c b/src/fstab-generator/fstab-generator.c index f7c8d11ace..87b8b77f22 100644 --- a/src/fstab-generator/fstab-generator.c +++ b/src/fstab-generator/fstab-generator.c @@ -248,6 +248,7 @@ static int add_mount( assert(what); assert(where); assert(opts); + assert(post); assert(source); if (streq_ptr(fstype, "autofs")) @@ -297,7 +298,7 @@ static int add_mount( "Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n", source); - if (post && !noauto && !nofail && !automount) + if (!noauto && !nofail && !automount) fprintf(f, "Before=%s\n", post); if (!automount && opts) { @@ -337,7 +338,7 @@ static int add_mount( if (r < 0) return log_error_errno(r, "Failed to write unit file %s: %m", unit); - if (!noauto && post) { + if (!noauto) { lnk = strjoin(arg_dest, "/", post, nofail || automount ? ".wants/" : ".requires/", name, NULL); if (!lnk) return log_oom(); @@ -368,10 +369,7 @@ static int add_mount( "Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n", source); - if (post) - fprintf(f, - "Before=%s\n", - post); + fprintf(f, "Before=%s\n", post); if (opts) { r = write_requires_after(f, opts); diff --git a/src/gpt-auto-generator/gpt-auto-generator.c b/src/gpt-auto-generator/gpt-auto-generator.c index 34852ce381..ce8cecc5cb 100644 --- a/src/gpt-auto-generator/gpt-auto-generator.c +++ b/src/gpt-auto-generator/gpt-auto-generator.c @@ -19,10 +19,10 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <unistd.h> +#include <blkid/blkid.h> #include <stdlib.h> #include <sys/statfs.h> -#include <blkid/blkid.h> +#include <unistd.h> #include "libudev.h" #include "sd-id128.h" diff --git a/src/hibernate-resume/hibernate-resume.c b/src/hibernate-resume/hibernate-resume.c index 316a2803d3..2e1259ef68 100644 --- a/src/hibernate-resume/hibernate-resume.c +++ b/src/hibernate-resume/hibernate-resume.c @@ -19,8 +19,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <stdio.h> #include <errno.h> +#include <stdio.h> #include <sys/stat.h> #include "alloc-util.h" diff --git a/src/hostname/hostnamed.c b/src/hostname/hostnamed.c index 92061532b8..dde2baf661 100644 --- a/src/hostname/hostnamed.c +++ b/src/hostname/hostnamed.c @@ -21,8 +21,8 @@ #include <errno.h> #include <string.h> -#include <unistd.h> #include <sys/utsname.h> +#include <unistd.h> #include "alloc-util.h" #include "bus-util.h" diff --git a/src/import/aufs-util.c b/src/import/aufs-util.c index 7b1ac134a0..82f519958c 100644 --- a/src/import/aufs-util.c +++ b/src/import/aufs-util.c @@ -21,10 +21,10 @@ #include <ftw.h> +#include "aufs-util.h" #include "rm-rf.h" #include "string-util.h" #include "util.h" -#include "aufs-util.h" static int nftw_cb( const char *fpath, diff --git a/src/import/curl-util.c b/src/import/curl-util.c index 4278466df1..8e531a64fa 100644 --- a/src/import/curl-util.c +++ b/src/import/curl-util.c @@ -48,9 +48,7 @@ static int curl_glue_on_io(sd_event_source *s, int fd, uint32_t revents, void *u assert(s); assert(g); - translated_fd = PTR_TO_INT(hashmap_get(g->translate_fds, INT_TO_PTR(fd+1))); - assert(translated_fd > 0); - translated_fd--; + translated_fd = PTR_TO_FD(hashmap_get(g->translate_fds, FD_TO_PTR(fd))); if ((revents & (EPOLLIN|EPOLLOUT)) == (EPOLLIN|EPOLLOUT)) action = CURL_POLL_INOUT; @@ -79,7 +77,7 @@ static int curl_glue_socket_callback(CURLM *curl, curl_socket_t s, int action, v assert(curl); assert(g); - io = hashmap_get(g->ios, INT_TO_PTR(s+1)); + io = hashmap_get(g->ios, FD_TO_PTR(s)); if (action == CURL_POLL_REMOVE) { if (io) { @@ -91,8 +89,8 @@ static int curl_glue_socket_callback(CURLM *curl, curl_socket_t s, int action, v sd_event_source_set_enabled(io, SD_EVENT_OFF); sd_event_source_unref(io); - hashmap_remove(g->ios, INT_TO_PTR(s+1)); - hashmap_remove(g->translate_fds, INT_TO_PTR(fd+1)); + hashmap_remove(g->ios, FD_TO_PTR(s)); + hashmap_remove(g->translate_fds, FD_TO_PTR(fd)); safe_close(fd); } @@ -143,17 +141,17 @@ static int curl_glue_socket_callback(CURLM *curl, curl_socket_t s, int action, v sd_event_source_set_description(io, "curl-io"); - r = hashmap_put(g->ios, INT_TO_PTR(s+1), io); + r = hashmap_put(g->ios, FD_TO_PTR(s), io); if (r < 0) { log_oom(); sd_event_source_unref(io); return -1; } - r = hashmap_put(g->translate_fds, INT_TO_PTR(fd+1), INT_TO_PTR(s+1)); + r = hashmap_put(g->translate_fds, FD_TO_PTR(fd), FD_TO_PTR(s)); if (r < 0) { log_oom(); - hashmap_remove(g->ios, INT_TO_PTR(s+1)); + hashmap_remove(g->ios, FD_TO_PTR(s)); sd_event_source_unref(io); return -1; } @@ -229,7 +227,7 @@ CurlGlue *curl_glue_unref(CurlGlue *g) { fd = sd_event_source_get_io_fd(io); assert(fd >= 0); - hashmap_remove(g->translate_fds, INT_TO_PTR(fd+1)); + hashmap_remove(g->translate_fds, FD_TO_PTR(fd)); safe_close(fd); sd_event_source_unref(io); diff --git a/src/import/curl-util.h b/src/import/curl-util.h index 6a2aa81c76..eec53c9266 100644 --- a/src/import/curl-util.h +++ b/src/import/curl-util.h @@ -21,8 +21,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <sys/types.h> #include <curl/curl.h> +#include <sys/types.h> #include "sd-event.h" diff --git a/src/import/export-raw.c b/src/import/export-raw.c index 103d45bf21..28c87594d6 100644 --- a/src/import/export-raw.c +++ b/src/import/export-raw.c @@ -20,6 +20,10 @@ ***/ #include <sys/sendfile.h> + +/* When we include libgen.h because we need dirname() we immediately + * undefine basename() since libgen.h defines it as a macro to the POSIX + * version which is really broken. We prefer GNU basename(). */ #include <libgen.h> #undef basename diff --git a/src/import/export-raw.h b/src/import/export-raw.h index b71de6cb82..e5e298f6ab 100644 --- a/src/import/export-raw.h +++ b/src/import/export-raw.h @@ -22,8 +22,9 @@ ***/ #include "sd-event.h" -#include "macro.h" + #include "import-compress.h" +#include "macro.h" typedef struct RawExport RawExport; diff --git a/src/import/export-tar.h b/src/import/export-tar.h index ce27a9fc1e..9061e7515d 100644 --- a/src/import/export-tar.h +++ b/src/import/export-tar.h @@ -22,8 +22,9 @@ ***/ #include "sd-event.h" -#include "macro.h" + #include "import-compress.h" +#include "macro.h" typedef struct TarExport TarExport; diff --git a/src/import/import-compress.h b/src/import/import-compress.h index 50d91f732c..0a13232554 100644 --- a/src/import/import-compress.h +++ b/src/import/import-compress.h @@ -21,11 +21,10 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <sys/types.h> - +#include <bzlib.h> #include <lzma.h> +#include <sys/types.h> #include <zlib.h> -#include <bzlib.h> #include "macro.h" diff --git a/src/import/import-raw.h b/src/import/import-raw.h index bf7c770340..626d965cf8 100644 --- a/src/import/import-raw.h +++ b/src/import/import-raw.h @@ -22,8 +22,9 @@ ***/ #include "sd-event.h" -#include "macro.h" + #include "import-util.h" +#include "macro.h" typedef struct RawImport RawImport; diff --git a/src/import/import-tar.h b/src/import/import-tar.h index aaecb51398..d12391572d 100644 --- a/src/import/import-tar.h +++ b/src/import/import-tar.h @@ -22,8 +22,9 @@ ***/ #include "sd-event.h" -#include "macro.h" + #include "import-util.h" +#include "macro.h" typedef struct TarImport TarImport; diff --git a/src/import/pull-common.c b/src/import/pull-common.c index d6567ba7ee..a83cffffa0 100644 --- a/src/import/pull-common.c +++ b/src/import/pull-common.c @@ -165,7 +165,7 @@ static int hash_url(const char *url, char **ret) { assert(url); - siphash24((uint8_t *) &h, url, strlen(url), k.bytes); + h = siphash24(url, strlen(url), k.bytes); if (asprintf(ret, "%"PRIx64, h) < 0) return -ENOMEM; diff --git a/src/import/pull-common.h b/src/import/pull-common.h index 7e6db1862c..ea228bb5c8 100644 --- a/src/import/pull-common.h +++ b/src/import/pull-common.h @@ -23,8 +23,8 @@ #include <stdbool.h> -#include "pull-job.h" #include "import-util.h" +#include "pull-job.h" int pull_make_local_copy(const char *final, const char *root, const char *local, bool force_local); diff --git a/src/import/pull-dkr.h b/src/import/pull-dkr.h index 33d18cb394..a95d91205b 100644 --- a/src/import/pull-dkr.h +++ b/src/import/pull-dkr.h @@ -22,6 +22,7 @@ #pragma once #include "sd-event.h" + #include "util.h" typedef enum { DKR_PULL_V1, DKR_PULL_V2 } DkrPullVersion; diff --git a/src/import/pull-job.h b/src/import/pull-job.h index 1777bf1c33..56a74a34ef 100644 --- a/src/import/pull-job.h +++ b/src/import/pull-job.h @@ -23,9 +23,9 @@ #include <gcrypt.h> -#include "macro.h" #include "curl-util.h" #include "import-compress.h" +#include "macro.h" typedef struct PullJob PullJob; diff --git a/src/import/pull-raw.h b/src/import/pull-raw.h index b03b4f5c92..0e4e1daf0e 100644 --- a/src/import/pull-raw.h +++ b/src/import/pull-raw.h @@ -22,8 +22,9 @@ ***/ #include "sd-event.h" -#include "macro.h" + #include "import-util.h" +#include "macro.h" typedef struct RawPull RawPull; diff --git a/src/import/pull-tar.c b/src/import/pull-tar.c index e7fcd293f1..2e48167c54 100644 --- a/src/import/pull-tar.c +++ b/src/import/pull-tar.c @@ -19,8 +19,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <sys/prctl.h> #include <curl/curl.h> +#include <sys/prctl.h> #include "sd-daemon.h" diff --git a/src/import/pull-tar.h b/src/import/pull-tar.h index 420845ae50..9f02f1ec71 100644 --- a/src/import/pull-tar.h +++ b/src/import/pull-tar.h @@ -22,8 +22,9 @@ ***/ #include "sd-event.h" -#include "macro.h" + #include "import-util.h" +#include "macro.h" typedef struct TarPull TarPull; diff --git a/src/initctl/initctl.c b/src/initctl/initctl.c index d4f8673187..a35f2b541c 100644 --- a/src/initctl/initctl.c +++ b/src/initctl/initctl.c @@ -19,11 +19,11 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <stdio.h> +#include <ctype.h> #include <errno.h> -#include <unistd.h> +#include <stdio.h> #include <sys/epoll.h> -#include <ctype.h> +#include <unistd.h> #include "sd-bus.h" #include "sd-daemon.h" diff --git a/src/journal-remote/journal-remote-parse.h b/src/journal-remote/journal-remote-parse.h index 14bfadc132..58cb5e70df 100644 --- a/src/journal-remote/journal-remote-parse.h +++ b/src/journal-remote/journal-remote-parse.h @@ -22,6 +22,7 @@ #pragma once #include "sd-event.h" + #include "journal-remote-write.h" typedef enum { diff --git a/src/journal-remote/journal-remote.h b/src/journal-remote/journal-remote.h index 6c2ccb9735..fd81a1c592 100644 --- a/src/journal-remote/journal-remote.h +++ b/src/journal-remote/journal-remote.h @@ -23,11 +23,11 @@ #include "sd-event.h" -#include "hashmap.h" -#include "microhttpd-util.h" +#include "hashmap.h" #include "journal-remote-parse.h" #include "journal-remote-write.h" +#include "microhttpd-util.h" typedef struct MHDDaemonWrapper MHDDaemonWrapper; diff --git a/src/journal-remote/journal-upload-journal.c b/src/journal-remote/journal-upload-journal.c index 3ee6d32bf7..a6d7c3b7e8 100644 --- a/src/journal-remote/journal-upload-journal.c +++ b/src/journal-remote/journal-upload-journal.c @@ -19,9 +19,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <stdbool.h> - #include <curl/curl.h> +#include <stdbool.h> #include "alloc-util.h" #include "journal-upload.h" diff --git a/src/journal-remote/journal-upload.h b/src/journal-remote/journal-upload.h index 3b46fa8cbf..b8cd04d527 100644 --- a/src/journal-remote/journal-upload.h +++ b/src/journal-remote/journal-upload.h @@ -2,8 +2,8 @@ #include <inttypes.h> -#include "sd-journal.h" #include "sd-event.h" +#include "sd-journal.h" typedef enum { ENTRY_CURSOR = 0, /* Nothing actually written yet. */ diff --git a/src/journal-remote/microhttpd-util.c b/src/journal-remote/microhttpd-util.c index b2c398a845..09e6da0031 100644 --- a/src/journal-remote/microhttpd-util.c +++ b/src/journal-remote/microhttpd-util.c @@ -32,10 +32,10 @@ #include "alloc-util.h" #include "log.h" #include "macro.h" +#include "microhttpd-util.h" #include "string-util.h" #include "strv.h" #include "util.h" -#include "microhttpd-util.h" void microhttpd_logger(void *arg, const char *fmt, va_list ap) { char *f; diff --git a/src/journal-remote/microhttpd-util.h b/src/journal-remote/microhttpd-util.h index b2feb9180a..3e8c4fa6d1 100644 --- a/src/journal-remote/microhttpd-util.h +++ b/src/journal-remote/microhttpd-util.h @@ -21,8 +21,8 @@ #pragma once -#include <stdarg.h> #include <microhttpd.h> +#include <stdarg.h> #include "macro.h" diff --git a/src/journal/audit-type.c b/src/journal/audit-type.c index 4888c7d05d..086bf7e7e3 100644 --- a/src/journal/audit-type.c +++ b/src/journal/audit-type.c @@ -25,8 +25,7 @@ # include <libaudit.h> #endif -#include "audit-type.h" -#include "macro.h" #include "missing.h" - +#include "audit-type.h" #include "audit_type-to-name.h" +#include "macro.h" diff --git a/src/journal/catalog.h b/src/journal/catalog.h index a72ecf6de7..bcc73c2631 100644 --- a/src/journal/catalog.h +++ b/src/journal/catalog.h @@ -24,6 +24,7 @@ #include <stdbool.h> #include "sd-id128.h" + #include "hashmap.h" #include "strbuf.h" diff --git a/src/journal/coredump-vacuum.c b/src/journal/coredump-vacuum.c index 39bc2e4270..09ab60c6c4 100644 --- a/src/journal/coredump-vacuum.c +++ b/src/journal/coredump-vacuum.c @@ -157,8 +157,7 @@ int coredump_vacuum(int exclude_fd, uint64_t keep_free, uint64_t max_use) { if (errno == ENOENT) return 0; - log_error_errno(errno, "Can't open coredump directory: %m"); - return -errno; + return log_error_errno(errno, "Can't open coredump directory: %m"); } for (;;) { @@ -183,7 +182,7 @@ int coredump_vacuum(int exclude_fd, uint64_t keep_free, uint64_t max_use) { if (errno == ENOENT) continue; - log_warning("Failed to stat /var/lib/systemd/coredump/%s", de->d_name); + log_warning_errno(errno, "Failed to stat /var/lib/systemd/coredump/%s: %m", de->d_name); continue; } @@ -201,7 +200,7 @@ int coredump_vacuum(int exclude_fd, uint64_t keep_free, uint64_t max_use) { t = timespec_load(&st.st_mtim); - c = hashmap_get(h, UINT32_TO_PTR(uid)); + c = hashmap_get(h, UID_TO_PTR(uid)); if (c) { if (t < c->oldest_mtime) { @@ -229,7 +228,7 @@ int coredump_vacuum(int exclude_fd, uint64_t keep_free, uint64_t max_use) { n->oldest_mtime = t; - r = hashmap_put(h, UINT32_TO_PTR(uid), n); + r = hashmap_put(h, UID_TO_PTR(uid), n); if (r < 0) return log_oom(); @@ -259,8 +258,7 @@ int coredump_vacuum(int exclude_fd, uint64_t keep_free, uint64_t max_use) { if (errno == ENOENT) continue; - log_error_errno(errno, "Failed to remove file %s: %m", worst->oldest_file); - return -errno; + return log_error_errno(errno, "Failed to remove file %s: %m", worst->oldest_file); } else log_info("Removed old coredump %s.", worst->oldest_file); } @@ -268,6 +266,5 @@ int coredump_vacuum(int exclude_fd, uint64_t keep_free, uint64_t max_use) { return 0; fail: - log_error_errno(errno, "Failed to read directory: %m"); - return -errno; + return log_error_errno(errno, "Failed to read directory: %m"); } diff --git a/src/journal/fsprg.h b/src/journal/fsprg.h index 5959b1fed2..b79221fc2e 100644 --- a/src/journal/fsprg.h +++ b/src/journal/fsprg.h @@ -25,8 +25,8 @@ * */ -#include <sys/types.h> #include <inttypes.h> +#include <sys/types.h> #include "macro.h" #include "util.h" diff --git a/src/journal/journal-authenticate.c b/src/journal/journal-authenticate.c index 0c4ac5cdc3..aeec83da1e 100644 --- a/src/journal/journal-authenticate.c +++ b/src/journal/journal-authenticate.c @@ -24,10 +24,10 @@ #include "fd-util.h" #include "fsprg.h" +#include "hexdecoct.h" #include "journal-authenticate.h" #include "journal-def.h" #include "journal-file.h" -#include "hexdecoct.h" static uint64_t journal_file_tag_seqnum(JournalFile *f) { uint64_t r; diff --git a/src/journal/journal-file.h b/src/journal/journal-file.h index 898d12d992..46c1f3278e 100644 --- a/src/journal/journal-file.h +++ b/src/journal/journal-file.h @@ -29,11 +29,11 @@ #include "sd-id128.h" -#include "sparse-endian.h" +#include "hashmap.h" #include "journal-def.h" #include "macro.h" #include "mmap-cache.h" -#include "hashmap.h" +#include "sparse-endian.h" typedef struct JournalMetrics { /* For all these: -1 means "pick automatically", and 0 means "no limit enforced" */ diff --git a/src/journal/journal-internal.h b/src/journal/journal-internal.h index 06847402e0..c3e75ad240 100644 --- a/src/journal/journal-internal.h +++ b/src/journal/journal-internal.h @@ -21,9 +21,9 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <sys/types.h> #include <inttypes.h> #include <stdbool.h> +#include <sys/types.h> #include "sd-id128.h" #include "sd-journal.h" diff --git a/src/journal/journal-qrcode.c b/src/journal/journal-qrcode.c index 1db66e89c6..257ddb302b 100644 --- a/src/journal/journal-qrcode.c +++ b/src/journal/journal-qrcode.c @@ -20,12 +20,11 @@ ***/ #include <assert.h> -#include <stdio.h> #include <errno.h> -#include <stdlib.h> -#include <stdbool.h> - #include <qrencode.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> #include "journal-qrcode.h" diff --git a/src/journal/journal-verify.c b/src/journal/journal-verify.c index 3676cb8788..715847e018 100644 --- a/src/journal/journal-verify.c +++ b/src/journal/journal-verify.c @@ -19,10 +19,10 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <unistd.h> -#include <sys/mman.h> #include <fcntl.h> #include <stddef.h> +#include <sys/mman.h> +#include <unistd.h> #include "alloc-util.h" #include "compress.h" diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c index 277adba904..d9a8063d31 100644 --- a/src/journal/journalctl.c +++ b/src/journal/journalctl.c @@ -133,6 +133,7 @@ static enum { ACTION_UPDATE_CATALOG, ACTION_LIST_BOOTS, ACTION_FLUSH, + ACTION_SYNC, ACTION_ROTATE, ACTION_VACUUM, } arg_action = ACTION_SHOW; @@ -201,7 +202,7 @@ static void help(void) { printf("%s [OPTIONS...] [MATCHES...]\n\n" "Query the journal.\n\n" - "Flags:\n" + "Options:\n" " --system Show the system journal\n" " --user Show the user journal for the current user\n" " -M --machine=CONTAINER Operate on local container\n" @@ -234,7 +235,7 @@ static void help(void) { " -m --merge Show entries from all available journals\n" " -D --directory=PATH Show journal files from directory\n" " --file=PATH Show journal file\n" - " --root=ROOT Operate on catalog files underneath the root ROOT\n" + " --root=ROOT Operate on catalog files below a root directory\n" #ifdef HAVE_GCRYPT " --interval=TIME Time interval for changing the FSS sealing key\n" " --verify-key=KEY Specify FSS verification key\n" @@ -244,20 +245,21 @@ static void help(void) { " -h --help Show this help text\n" " --version Show package version\n" " -F --field=FIELD List all values that a specified field takes\n" - " --new-id128 Generate a new 128-bit ID\n" " --disk-usage Show total disk usage of all journal files\n" " --vacuum-size=BYTES Reduce disk usage below specified size\n" " --vacuum-files=INT Leave only the specified number of journal files\n" " --vacuum-time=TIME Remove journal files older than specified time\n" + " --verify Verify journal file consistency\n" + " --sync Synchronize unwritten journal messages to disk\n" " --flush Flush all journal data from /run into /var\n" " --rotate Request immediate rotation of the journal files\n" " --header Show journal header information\n" " --list-catalog Show all message IDs in the catalog\n" " --dump-catalog Show entries in the message catalog\n" " --update-catalog Update the message catalog database\n" + " --new-id128 Generate a new 128-bit ID\n" #ifdef HAVE_GCRYPT " --setup-keys Generate a new FSS key pair\n" - " --verify Verify journal file consistency\n" #endif , program_invocation_short_name); } @@ -289,6 +291,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_UPDATE_CATALOG, ARG_FORCE, ARG_UTC, + ARG_SYNC, ARG_FLUSH, ARG_ROTATE, ARG_VACUUM_SIZE, @@ -345,6 +348,7 @@ static int parse_argv(int argc, char *argv[]) { { "machine", required_argument, NULL, 'M' }, { "utc", no_argument, NULL, ARG_UTC }, { "flush", no_argument, NULL, ARG_FLUSH }, + { "sync", no_argument, NULL, ARG_SYNC }, { "rotate", no_argument, NULL, ARG_ROTATE }, { "vacuum-size", required_argument, NULL, ARG_VACUUM_SIZE }, { "vacuum-files", required_argument, NULL, ARG_VACUUM_FILES }, @@ -729,6 +733,10 @@ static int parse_argv(int argc, char *argv[]) { arg_action = ACTION_ROTATE; break; + case ARG_SYNC: + arg_action = ACTION_SYNC; + break; + case '?': return -EINVAL; @@ -1763,6 +1771,11 @@ static int flush_to_var(void) { _cleanup_close_ int watch_fd = -1; int r; + if (arg_machine) { + log_error("--flush is not supported in conjunction with --machine=."); + return -EOPNOTSUPP; + } + /* Quick exit */ if (access("/run/systemd/journal/flushed", F_OK) >= 0) return 0; @@ -1782,10 +1795,8 @@ static int flush_to_var(void) { &error, NULL, "ssi", "systemd-journald.service", "main", SIGUSR1); - if (r < 0) { - log_error("Failed to kill journal service: %s", bus_error_message(&error, r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to kill journal service: %s", bus_error_message(&error, r)); mkdir_p("/run/systemd/journal", 0755); @@ -1816,30 +1827,97 @@ static int flush_to_var(void) { return 0; } -static int rotate(void) { - _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; +static int send_signal_and_wait(int sig, const char *watch_path) { _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL; + _cleanup_close_ int watch_fd = -1; + usec_t start; int r; - r = bus_connect_system_systemd(&bus); - if (r < 0) - return log_error_errno(r, "Failed to get D-Bus connection: %m"); + if (arg_machine) { + log_error("--sync and --rotate are not supported in conjunction with --machine=."); + return -EOPNOTSUPP; + } - r = sd_bus_call_method( - bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "KillUnit", - &error, - NULL, - "ssi", "systemd-journald.service", "main", SIGUSR2); - if (r < 0) - return log_error_errno(r, "Failed to kill journal service: %s", bus_error_message(&error, r)); + start = now(CLOCK_MONOTONIC); + + /* This call sends the specified signal to journald, and waits + * for acknowledgment by watching the mtime of the specified + * flag file. This is used to trigger syncing or rotation and + * then wait for the operation to complete. */ + + for (;;) { + usec_t tstamp; + + /* See if a sync happened by now. */ + r = read_timestamp_file(watch_path, &tstamp); + if (r < 0 && r != -ENOENT) + return log_error_errno(errno, "Failed to read %s: %m", watch_path); + if (r >= 0 && tstamp >= start) + return 0; + + /* Let's ask for a sync, but only once. */ + if (!bus) { + _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; + + r = bus_connect_system_systemd(&bus); + if (r < 0) + return log_error_errno(r, "Failed to get D-Bus connection: %m"); + + r = sd_bus_call_method( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "KillUnit", + &error, + NULL, + "ssi", "systemd-journald.service", "main", sig); + if (r < 0) + return log_error_errno(r, "Failed to kill journal service: %s", bus_error_message(&error, r)); + + continue; + } + + /* Let's install the inotify watch, if we didn't do that yet. */ + if (watch_fd < 0) { + + mkdir_p("/run/systemd/journal", 0755); + + watch_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC); + if (watch_fd < 0) + return log_error_errno(errno, "Failed to create inotify watch: %m"); + + r = inotify_add_watch(watch_fd, "/run/systemd/journal", IN_MOVED_TO|IN_DONT_FOLLOW|IN_ONLYDIR); + if (r < 0) + return log_error_errno(errno, "Failed to watch journal directory: %m"); + + /* Recheck the flag file immediately, so that we don't miss any event since the last check. */ + continue; + } + + /* OK, all preparatory steps done, let's wait until + * inotify reports an event. */ + + r = fd_wait_for_event(watch_fd, POLLIN, USEC_INFINITY); + if (r < 0) + return log_error_errno(r, "Failed to wait for event: %m"); + + r = flush_fd(watch_fd); + if (r < 0) + return log_error_errno(r, "Failed to flush inotify events: %m"); + } return 0; } +static int rotate(void) { + return send_signal_and_wait(SIGUSR2, "/run/systemd/journal/rotated"); +} + +static int sync_journal(void) { + return send_signal_and_wait(SIGRTMIN+1, "/run/systemd/journal/synced"); +} + int main(int argc, char *argv[]) { int r; _cleanup_journal_close_ sd_journal *j = NULL; @@ -1865,30 +1943,19 @@ int main(int argc, char *argv[]) { * be split up into many files. */ setrlimit_closest(RLIMIT_NOFILE, &RLIMIT_MAKE_CONST(16384)); - if (arg_action == ACTION_NEW_ID128) { - r = generate_new_id128(); - goto finish; - } + switch (arg_action) { - if (arg_action == ACTION_FLUSH) { - r = flush_to_var(); - goto finish; - } - - if (arg_action == ACTION_ROTATE) { - r = rotate(); + case ACTION_NEW_ID128: + r = generate_new_id128(); goto finish; - } - if (arg_action == ACTION_SETUP_KEYS) { + case ACTION_SETUP_KEYS: r = setup_keys(); goto finish; - } - - if (arg_action == ACTION_UPDATE_CATALOG || - arg_action == ACTION_LIST_CATALOG || - arg_action == ACTION_DUMP_CATALOG) { + case ACTION_LIST_CATALOG: + case ACTION_DUMP_CATALOG: + case ACTION_UPDATE_CATALOG: { _cleanup_free_ char *database; database = path_join(arg_root, CATALOG_DATABASE, NULL); @@ -1905,9 +1972,9 @@ int main(int argc, char *argv[]) { bool oneline = arg_action == ACTION_LIST_CATALOG; pager_open_if_enabled(); + if (optind < argc) - r = catalog_list_items(stdout, database, - oneline, argv + optind); + r = catalog_list_items(stdout, database, oneline, argv + optind); else r = catalog_list(stdout, database, oneline); if (r < 0) @@ -1917,6 +1984,31 @@ int main(int argc, char *argv[]) { goto finish; } + case ACTION_FLUSH: + r = flush_to_var(); + goto finish; + + case ACTION_SYNC: + r = sync_journal(); + goto finish; + + case ACTION_ROTATE: + r = rotate(); + goto finish; + + case ACTION_SHOW: + case ACTION_PRINT_HEADER: + case ACTION_VERIFY: + case ACTION_DISK_USAGE: + case ACTION_LIST_BOOTS: + case ACTION_VACUUM: + /* These ones require access to the journal files, continue below. */ + break; + + default: + assert_not_reached("Unknown action"); + } + if (arg_directory) r = sd_journal_open_directory(&j, arg_directory, arg_journal_type); else if (arg_file) @@ -1926,8 +2018,7 @@ int main(int argc, char *argv[]) { else r = sd_journal_open(&j, !arg_merge*SD_JOURNAL_LOCAL_ONLY + arg_journal_type); if (r < 0) { - log_error_errno(r, "Failed to open %s: %m", - arg_directory ? arg_directory : arg_file ? "files" : "journal"); + log_error_errno(r, "Failed to open %s: %m", arg_directory ?: arg_file ? "files" : "journal"); goto finish; } @@ -1935,18 +2026,28 @@ int main(int argc, char *argv[]) { if (r < 0) goto finish; - if (arg_action == ACTION_VERIFY) { - r = verify(j); - goto finish; - } + switch (arg_action) { + + case ACTION_NEW_ID128: + case ACTION_SETUP_KEYS: + case ACTION_LIST_CATALOG: + case ACTION_DUMP_CATALOG: + case ACTION_UPDATE_CATALOG: + case ACTION_FLUSH: + case ACTION_SYNC: + case ACTION_ROTATE: + assert_not_reached("Unexpected action."); - if (arg_action == ACTION_PRINT_HEADER) { + case ACTION_PRINT_HEADER: journal_print_header(j); r = 0; goto finish; - } - if (arg_action == ACTION_DISK_USAGE) { + case ACTION_VERIFY: + r = verify(j); + goto finish; + + case ACTION_DISK_USAGE: { uint64_t bytes = 0; char sbytes[FORMAT_BYTES_MAX]; @@ -1959,7 +2060,11 @@ int main(int argc, char *argv[]) { goto finish; } - if (arg_action == ACTION_VACUUM) { + case ACTION_LIST_BOOTS: + r = list_boots(j); + goto finish; + + case ACTION_VACUUM: { Directory *d; Iterator i; @@ -1979,9 +2084,11 @@ int main(int argc, char *argv[]) { goto finish; } - if (arg_action == ACTION_LIST_BOOTS) { - r = list_boots(j); - goto finish; + case ACTION_SHOW: + break; + + default: + assert_not_reached("Unknown action"); } /* add_boot() must be called first! @@ -2140,7 +2247,8 @@ int main(int argc, char *argv[]) { if (arg_follow) need_seek = true; else { - printf("-- No entries --\n"); + if (!arg_quiet) + printf("-- No entries --\n"); goto finish; } } diff --git a/src/journal/journald-audit.h b/src/journal/journald-audit.h index 68cdfb3410..5c88bb6383 100644 --- a/src/journal/journald-audit.h +++ b/src/journal/journald-audit.h @@ -21,8 +21,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include "socket-util.h" #include "journald-server.h" +#include "socket-util.h" void server_process_audit_message(Server *s, const void *buffer, size_t buffer_size, const struct ucred *ucred, const union sockaddr_union *sa, socklen_t salen); diff --git a/src/journal/journald-console.c b/src/journal/journald-console.c index 89f3d4b42f..04487c29b5 100644 --- a/src/journal/journald-console.c +++ b/src/journal/journald-console.c @@ -19,9 +19,9 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <time.h> #include <fcntl.h> #include <sys/socket.h> +#include <time.h> #include "alloc-util.h" #include "fd-util.h" diff --git a/src/journal/journald-native.c b/src/journal/journald-native.c index 1e3774dafb..69a685c06f 100644 --- a/src/journal/journald-native.c +++ b/src/journal/journald-native.c @@ -22,6 +22,7 @@ #include <stddef.h> #include <sys/epoll.h> #include <sys/mman.h> +#include <sys/statvfs.h> #include <unistd.h> #include "alloc-util.h" @@ -399,8 +400,37 @@ void server_process_native_file( assert_se(munmap(p, ps) >= 0); } else { _cleanup_free_ void *p = NULL; + struct statvfs vfs; ssize_t n; + if (fstatvfs(fd, &vfs) < 0) { + log_error_errno(errno, "Failed to stat file system of passed file, ignoring: %m"); + return; + } + + /* Refuse operating on file systems that have + * mandatory locking enabled, see: + * + * https://github.com/systemd/systemd/issues/1822 + */ + if (vfs.f_flag & ST_MANDLOCK) { + log_error("Received file descriptor from file system with mandatory locking enable, refusing."); + return; + } + + /* Make the fd non-blocking. On regular files this has + * the effect of bypassing mandatory locking. Of + * course, this should normally not be necessary given + * the check above, but let's better be safe than + * sorry, after all NFS is pretty confusing regarding + * file system flags, and we better don't trust it, + * and so is SMB. */ + r = fd_nonblock(fd, true); + if (r < 0) { + log_error_errno(r, "Failed to make fd non-blocking, ignoring: %m"); + return; + } + /* The file is not sealed, we can't map the file here, since * clients might then truncate it and trigger a SIGBUS for * us. So let's stupidly read it */ diff --git a/src/journal/journald-rate-limit.c b/src/journal/journald-rate-limit.c index 434ddc8ac9..1c406aef8e 100644 --- a/src/journal/journald-rate-limit.c +++ b/src/journal/journald-rate-limit.c @@ -24,11 +24,11 @@ #include "alloc-util.h" #include "hashmap.h" +#include "journald-rate-limit.h" #include "list.h" #include "random-util.h" #include "string-util.h" #include "util.h" -#include "journald-rate-limit.h" #define POOLS_MAX 5 #define BUCKETS_MAX 127 @@ -162,7 +162,7 @@ static JournalRateLimitGroup* journal_rate_limit_group_new(JournalRateLimit *r, siphash24_init(&state, r->hash_key); string_hash_func(g->id, &state); - siphash24_finalize((uint8_t*)&g->hash, &state); + g->hash = siphash24_finalize(&state); journal_rate_limit_vacuum(r, ts); @@ -230,7 +230,7 @@ int journal_rate_limit_test(JournalRateLimit *r, const char *id, int priority, u siphash24_init(&state, r->hash_key); string_hash_func(id, &state); - siphash24_finalize((uint8_t*)&h, &state); + h = siphash24_finalize(&state); g = r->buckets[h % BUCKETS_MAX]; LIST_FOREACH(bucket, g, g) diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c index 36fe739073..7d11a568aa 100644 --- a/src/journal/journald-server.c +++ b/src/journal/journald-server.c @@ -41,6 +41,7 @@ #include "dirent-util.h" #include "extract-word.h" #include "fd-util.h" +#include "fileio.h" #include "formats-util.h" #include "fs-util.h" #include "hashmap.h" @@ -68,6 +69,7 @@ #include "socket-util.h" #include "string-table.h" #include "string-util.h" +#include "user-util.h" #define USER_JOURNALS_MAX 1024 @@ -280,7 +282,7 @@ static JournalFile* find_journal(Server *s, uid_t uid) { if (r < 0) return s->system_journal; - f = ordered_hashmap_get(s->user_journals, UINT32_TO_PTR(uid)); + f = ordered_hashmap_get(s->user_journals, UID_TO_PTR(uid)); if (f) return f; @@ -301,7 +303,7 @@ static JournalFile* find_journal(Server *s, uid_t uid) { server_fix_perms(s, f, uid); - r = ordered_hashmap_put(s->user_journals, UINT32_TO_PTR(uid), f); + r = ordered_hashmap_put(s->user_journals, UID_TO_PTR(uid), f); if (r < 0) { journal_file_close(f); return s->system_journal; @@ -347,7 +349,7 @@ void server_rotate(Server *s) { (void) do_rotate(s, &s->system_journal, "system", s->seal, 0); ORDERED_HASHMAP_FOREACH_KEY(f, k, s->user_journals, i) { - r = do_rotate(s, &f, "user", s->seal, PTR_TO_UINT32(k)); + r = do_rotate(s, &f, "user", s->seal, PTR_TO_UID(k)); if (r >= 0) ordered_hashmap_replace(s->user_journals, k, f); else if (!f) @@ -358,7 +360,6 @@ void server_rotate(Server *s) { void server_sync(Server *s) { JournalFile *f; - void *k; Iterator i; int r; @@ -368,7 +369,7 @@ void server_sync(Server *s) { log_warning_errno(r, "Failed to sync system journal, ignoring: %m"); } - ORDERED_HASHMAP_FOREACH_KEY(f, k, s->user_journals, i) { + ORDERED_HASHMAP_FOREACH(f, s->user_journals, i) { r = journal_file_set_offline(f); if (r < 0) log_warning_errno(r, "Failed to sync user journal, ignoring: %m"); @@ -1240,29 +1241,38 @@ int server_process_datagram(sd_event_source *es, int fd, uint32_t revents, void static int dispatch_sigusr1(sd_event_source *es, const struct signalfd_siginfo *si, void *userdata) { Server *s = userdata; + int r; assert(s); - log_info("Received request to flush runtime journal from PID %"PRIu32, si->ssi_pid); + log_info("Received request to flush runtime journal from PID " PID_FMT, si->ssi_pid); server_flush_to_var(s); server_sync(s); server_vacuum(s, false, false); - (void) touch("/run/systemd/journal/flushed"); + r = touch("/run/systemd/journal/flushed"); + if (r < 0) + log_warning_errno(r, "Failed to touch /run/systemd/journal/flushed, ignoring: %m"); return 0; } static int dispatch_sigusr2(sd_event_source *es, const struct signalfd_siginfo *si, void *userdata) { Server *s = userdata; + int r; assert(s); - log_info("Received request to rotate journal from PID %"PRIu32, si->ssi_pid); + log_info("Received request to rotate journal from PID " PID_FMT, si->ssi_pid); server_rotate(s); server_vacuum(s, true, true); + /* Let clients know when the most recent rotation happened. */ + r = write_timestamp_file_atomic("/run/systemd/journal/rotated", now(CLOCK_MONOTONIC)); + if (r < 0) + log_warning_errno(r, "Failed to write /run/systemd/journal/rotated, ignoring: %m"); + return 0; } @@ -1277,12 +1287,30 @@ static int dispatch_sigterm(sd_event_source *es, const struct signalfd_siginfo * return 0; } +static int dispatch_sigrtmin1(sd_event_source *es, const struct signalfd_siginfo *si, void *userdata) { + Server *s = userdata; + int r; + + assert(s); + + log_debug("Received request to sync from PID " PID_FMT, si->ssi_pid); + + server_sync(s); + + /* Let clients know when the most recent sync happened. */ + r = write_timestamp_file_atomic("/run/systemd/journal/synced", now(CLOCK_MONOTONIC)); + if (r < 0) + log_warning_errno(r, "Failed to write /run/systemd/journal/synced, ignoring: %m"); + + return 0; +} + static int setup_signals(Server *s) { int r; assert(s); - assert(sigprocmask_many(SIG_SETMASK, NULL, SIGINT, SIGTERM, SIGUSR1, SIGUSR2, -1) >= 0); + assert(sigprocmask_many(SIG_SETMASK, NULL, SIGINT, SIGTERM, SIGUSR1, SIGUSR2, SIGRTMIN+1, -1) >= 0); r = sd_event_add_signal(s->event, &s->sigusr1_event_source, SIGUSR1, dispatch_sigusr1, s); if (r < 0) @@ -1312,6 +1340,19 @@ static int setup_signals(Server *s) { if (r < 0) return r; + /* SIGRTMIN+1 causes an immediate sync. We process this very + * late, so that everything else queued at this point is + * really written to disk. Clients can watch + * /run/systemd/journal/synced with inotify until its mtime + * changes to see when a sync happened. */ + r = sd_event_add_signal(s->event, &s->sigrtmin1_event_source, SIGRTMIN+1, dispatch_sigrtmin1, s); + if (r < 0) + return r; + + r = sd_event_source_set_priority(s->sigrtmin1_event_source, SD_EVENT_PRIORITY_NORMAL+15); + if (r < 0) + return r; + return 0; } @@ -1484,11 +1525,6 @@ static int dispatch_notify_event(sd_event_source *es, int fd, uint32_t revents, assert(s->notify_event_source == es); assert(s->notify_fd == fd); - if (revents != EPOLLOUT) { - log_error("Invalid events on notify file descriptor."); - return -EINVAL; - } - /* The $NOTIFY_SOCKET is writable again, now send exactly one * message on it. Either it's the wtachdog event, the initial * READY=1 event or an stdout stream event. If there's nothing @@ -1634,7 +1670,7 @@ static int server_connect_notify(Server *s) { if (sd_watchdog_enabled(false, &s->watchdog_usec) > 0) { s->send_watchdog = true; - r = sd_event_add_time(s->event, &s->watchdog_event_source, CLOCK_MONOTONIC, now(CLOCK_MONOTONIC) + s->watchdog_usec/2, s->watchdog_usec*3/4, dispatch_watchdog, s); + r = sd_event_add_time(s->event, &s->watchdog_event_source, CLOCK_MONOTONIC, now(CLOCK_MONOTONIC) + s->watchdog_usec/2, s->watchdog_usec/4, dispatch_watchdog, s); if (r < 0) return log_error_errno(r, "Failed to add watchdog time event: %m"); } @@ -1874,6 +1910,7 @@ void server_done(Server *s) { sd_event_source_unref(s->sigusr2_event_source); sd_event_source_unref(s->sigterm_event_source); sd_event_source_unref(s->sigint_event_source); + sd_event_source_unref(s->sigrtmin1_event_source); sd_event_source_unref(s->hostname_event_source); sd_event_source_unref(s->notify_event_source); sd_event_source_unref(s->watchdog_event_source); diff --git a/src/journal/journald-server.h b/src/journal/journald-server.h index 03a61bd2ed..dcc21bb7c3 100644 --- a/src/journal/journald-server.h +++ b/src/journal/journald-server.h @@ -72,6 +72,7 @@ struct Server { sd_event_source *sigusr2_event_source; sd_event_source *sigterm_event_source; sd_event_source *sigint_event_source; + sd_event_source *sigrtmin1_event_source; sd_event_source *hostname_event_source; sd_event_source *notify_event_source; sd_event_source *watchdog_event_source; diff --git a/src/journal/journald-wall.c b/src/journal/journald-wall.c index 69540f1141..88bea3b86e 100644 --- a/src/journal/journald-wall.c +++ b/src/journal/journald-wall.c @@ -22,10 +22,10 @@ #include "alloc-util.h" #include "formats-util.h" #include "journald-server.h" +#include "journald-wall.h" #include "process-util.h" #include "string-util.h" #include "utmp-wtmp.h" -#include "journald-wall.h" void server_forward_wall( Server *s, diff --git a/src/journal/journald.c b/src/journal/journald.c index b137e3c7be..b9f5c099e1 100644 --- a/src/journal/journald.c +++ b/src/journal/journald.c @@ -21,16 +21,15 @@ #include <unistd.h> -#include "sd-messages.h" #include "sd-daemon.h" +#include "sd-messages.h" +#include "formats-util.h" #include "journal-authenticate.h" -#include "journald-server.h" #include "journald-kmsg.h" +#include "journald-server.h" #include "journald-syslog.h" - #include "sigbus.h" -#include "formats-util.h" int main(int argc, char *argv[]) { Server server; diff --git a/src/journal/lookup3.c b/src/journal/lookup3.c index 52ffdf7b1d..3d791234f4 100644 --- a/src/journal/lookup3.c +++ b/src/journal/lookup3.c @@ -40,10 +40,10 @@ on 1 byte), but shoehorning those bytes into integers efficiently is messy. */ /* #define SELF_TEST 1 */ -#include <stdio.h> /* defines printf for tests */ -#include <time.h> /* defines time_t for timings in the test */ #include <stdint.h> /* defines uint32_t etc */ +#include <stdio.h> /* defines printf for tests */ #include <sys/param.h> /* attempt to define endianness */ +#include <time.h> /* defines time_t for timings in the test */ #ifdef linux # include <endian.h> /* attempt to define endianness */ #endif diff --git a/src/journal/mmap-cache.c b/src/journal/mmap-cache.c index 3cb1dfa986..5a07ddda76 100644 --- a/src/journal/mmap-cache.c +++ b/src/journal/mmap-cache.c @@ -24,13 +24,14 @@ #include <sys/mman.h> #include "alloc-util.h" +#include "fd-util.h" #include "hashmap.h" #include "list.h" #include "log.h" -#include "util.h" #include "macro.h" -#include "sigbus.h" #include "mmap-cache.h" +#include "sigbus.h" +#include "util.h" typedef struct Window Window; typedef struct Context Context; @@ -289,7 +290,7 @@ static void fd_free(FileDescriptor *f) { window_free(f->windows); if (f->cache) - assert_se(hashmap_remove(f->cache->fds, INT_TO_PTR(f->fd + 1))); + assert_se(hashmap_remove(f->cache->fds, FD_TO_PTR(f->fd))); free(f); } @@ -301,7 +302,7 @@ static FileDescriptor* fd_add(MMapCache *m, int fd) { assert(m); assert(fd >= 0); - f = hashmap_get(m->fds, INT_TO_PTR(fd + 1)); + f = hashmap_get(m->fds, FD_TO_PTR(fd)); if (f) return f; @@ -316,7 +317,7 @@ static FileDescriptor* fd_add(MMapCache *m, int fd) { f->cache = m; f->fd = fd; - r = hashmap_put(m->fds, UINT_TO_PTR(fd + 1), f); + r = hashmap_put(m->fds, FD_TO_PTR(fd), f); if (r < 0) { free(f); return NULL; @@ -429,7 +430,7 @@ static int find_mmap( assert(fd >= 0); assert(size > 0); - f = hashmap_get(m->fds, INT_TO_PTR(fd + 1)); + f = hashmap_get(m->fds, FD_TO_PTR(fd)); if (!f) return 0; @@ -679,7 +680,7 @@ bool mmap_cache_got_sigbus(MMapCache *m, int fd) { mmap_cache_process_sigbus(m); - f = hashmap_get(m->fds, INT_TO_PTR(fd + 1)); + f = hashmap_get(m->fds, FD_TO_PTR(fd)); if (!f) return false; @@ -698,7 +699,7 @@ void mmap_cache_close_fd(MMapCache *m, int fd) { mmap_cache_process_sigbus(m); - f = hashmap_get(m->fds, INT_TO_PTR(fd + 1)); + f = hashmap_get(m->fds, FD_TO_PTR(fd)); if (!f) return; diff --git a/src/journal/test-catalog.c b/src/journal/test-catalog.c index aea8fd15e6..25980b7744 100644 --- a/src/journal/test-catalog.c +++ b/src/journal/test-catalog.c @@ -20,10 +20,10 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <locale.h> -#include <unistd.h> #include <errno.h> #include <fcntl.h> +#include <locale.h> +#include <unistd.h> #include "sd-messages.h" diff --git a/src/journal/test-journal-enum.c b/src/journal/test-journal-enum.c index 040c7d58fb..8f56ea1907 100644 --- a/src/journal/test-journal-enum.c +++ b/src/journal/test-journal-enum.c @@ -23,9 +23,9 @@ #include "sd-journal.h" +#include "journal-internal.h" #include "log.h" #include "macro.h" -#include "journal-internal.h" int main(int argc, char *argv[]) { unsigned n = 0; diff --git a/src/journal/test-journal-interleaving.c b/src/journal/test-journal-interleaving.c index 4ad89fe4b6..5c055ef748 100644 --- a/src/journal/test-journal-interleaving.c +++ b/src/journal/test-journal-interleaving.c @@ -20,8 +20,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <unistd.h> #include <fcntl.h> +#include <unistd.h> #include "sd-journal.h" diff --git a/src/journal/test-journal-verify.c b/src/journal/test-journal-verify.c index 887a83efe1..a7abb11fba 100644 --- a/src/journal/test-journal-verify.c +++ b/src/journal/test-journal-verify.c @@ -19,9 +19,9 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <fcntl.h> #include <stdio.h> #include <unistd.h> -#include <fcntl.h> #include "fd-util.h" #include "journal-file.h" diff --git a/src/journal/test-journal.c b/src/journal/test-journal.c index 01d4bc968a..266e0d5473 100644 --- a/src/journal/test-journal.c +++ b/src/journal/test-journal.c @@ -22,11 +22,11 @@ #include <fcntl.h> #include <unistd.h> -#include "log.h" -#include "rm-rf.h" -#include "journal-file.h" #include "journal-authenticate.h" +#include "journal-file.h" #include "journal-vacuum.h" +#include "log.h" +#include "rm-rf.h" static bool arg_keep = false; diff --git a/src/libsystemd-network/arp-util.h b/src/libsystemd-network/arp-util.h index 44e5c893a7..63c559f8dd 100644 --- a/src/libsystemd-network/arp-util.h +++ b/src/libsystemd-network/arp-util.h @@ -23,8 +23,8 @@ #include <netinet/if_ether.h> -#include "sparse-endian.h" #include "socket-util.h" +#include "sparse-endian.h" int arp_network_bind_raw_socket(int index, be32_t address, const struct ether_addr *eth_mac); diff --git a/src/libsystemd-network/dhcp-identifier.c b/src/libsystemd-network/dhcp-identifier.c index 51ee7bcce4..d7ae865557 100644 --- a/src/libsystemd-network/dhcp-identifier.c +++ b/src/libsystemd-network/dhcp-identifier.c @@ -35,6 +35,7 @@ int dhcp_identifier_set_duid_en(struct duid *duid, size_t *len) { sd_id128_t machine_id; + uint64_t hash; int r; assert(duid); @@ -50,8 +51,9 @@ int dhcp_identifier_set_duid_en(struct duid *duid, size_t *len) { *len = sizeof(duid->type) + sizeof(duid->en); /* a bit of snake-oil perhaps, but no need to expose the machine-id - directly */ - siphash24(duid->en.id, &machine_id, sizeof(machine_id), HASH_KEY.bytes); + directly; duid->en.id might not be aligned, so we need to copy */ + hash = htole64(siphash24(&machine_id, sizeof(machine_id), HASH_KEY.bytes)); + memcpy(duid->en.id, &hash, sizeof(duid->en.id)); return 0; } @@ -84,10 +86,12 @@ int dhcp_identifier_set_iaid(int ifindex, uint8_t *mac, size_t mac_len, void *_i } if (name) - siphash24((uint8_t*)&id, name, strlen(name), HASH_KEY.bytes); + id = siphash24(name, strlen(name), HASH_KEY.bytes); else /* fall back to MAC address if no predictable name available */ - siphash24((uint8_t*)&id, mac, mac_len, HASH_KEY.bytes); + id = siphash24(mac, mac_len, HASH_KEY.bytes); + + id = htole64(id); /* fold into 32 bits */ unaligned_write_be32(_id, (id & 0xffffffff) ^ (id >> 32)); diff --git a/src/libsystemd-network/dhcp-internal.h b/src/libsystemd-network/dhcp-internal.h index a5daaa543a..7038212bcf 100644 --- a/src/libsystemd-network/dhcp-internal.h +++ b/src/libsystemd-network/dhcp-internal.h @@ -47,8 +47,7 @@ int dhcp_option_append(DHCPMessage *message, size_t size, size_t *offset, uint8_ typedef int (*dhcp_option_cb_t)(uint8_t code, uint8_t len, const void *option, void *userdata); -int dhcp_option_parse(DHCPMessage *message, size_t len, - dhcp_option_cb_t cb, void *userdata); +int dhcp_option_parse(DHCPMessage *message, size_t len, dhcp_option_cb_t cb, void *userdata, char **error_message); int dhcp_message_init(DHCPMessage *message, uint8_t op, uint32_t xid, uint8_t type, uint16_t arp_type, size_t optlen, diff --git a/src/libsystemd-network/dhcp-lease-internal.h b/src/libsystemd-network/dhcp-lease-internal.h index c6b97ca8f7..138bdd9691 100644 --- a/src/libsystemd-network/dhcp-lease-internal.h +++ b/src/libsystemd-network/dhcp-lease-internal.h @@ -25,12 +25,11 @@ #include <stdint.h> #include <linux/if_packet.h> -#include "util.h" -#include "list.h" +#include "sd-dhcp-client.h" #include "dhcp-protocol.h" - -#include "sd-dhcp-client.h" +#include "list.h" +#include "util.h" struct sd_dhcp_route { struct in_addr dst_addr; diff --git a/src/libsystemd-network/dhcp-option.c b/src/libsystemd-network/dhcp-option.c index 36be7d54ed..1de7f3639c 100644 --- a/src/libsystemd-network/dhcp-option.c +++ b/src/libsystemd-network/dhcp-option.c @@ -19,10 +19,13 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <stdint.h> -#include <string.h> #include <errno.h> +#include <stdint.h> #include <stdio.h> +#include <string.h> + +#include "alloc-util.h" +#include "utf8.h" #include "dhcp-internal.h" @@ -139,72 +142,84 @@ int dhcp_option_append(DHCPMessage *message, size_t size, size_t *offset, } static int parse_options(const uint8_t options[], size_t buflen, uint8_t *overload, - uint8_t *message_type, dhcp_option_cb_t cb, + uint8_t *message_type, char **error_message, dhcp_option_cb_t cb, void *userdata) { uint8_t code, len; + const uint8_t *option; size_t offset = 0; while (offset < buflen) { - switch (options[offset]) { - case DHCP_OPTION_PAD: - offset++; + code = options[offset ++]; - break; + switch (code) { + case DHCP_OPTION_PAD: + continue; case DHCP_OPTION_END: return 0; + } - case DHCP_OPTION_MESSAGE_TYPE: - if (buflen < offset + 3) - return -ENOBUFS; + if (buflen < offset + 1) + return -ENOBUFS; + + len = options[offset ++]; + + if (buflen < offset + len) + return -EINVAL; + + option = &options[offset]; - len = options[++offset]; + switch (code) { + case DHCP_OPTION_MESSAGE_TYPE: if (len != 1) return -EINVAL; if (message_type) - *message_type = options[++offset]; - else - offset++; - - offset++; + *message_type = *option; break; - case DHCP_OPTION_OVERLOAD: - if (buflen < offset + 3) - return -ENOBUFS; - - len = options[++offset]; - if (len != 1) + case DHCP_OPTION_ERROR_MESSAGE: + if (len == 0) return -EINVAL; - if (overload) - *overload = options[++offset]; - else - offset++; + if (error_message) { + _cleanup_free_ char *string = NULL; - offset++; + /* Accept a trailing NUL byte */ + if (memchr(option, 0, len - 1)) + return -EINVAL; - break; + string = strndup((const char *) option, len); + if (!string) + return -ENOMEM; - default: - if (buflen < offset + 3) - return -ENOBUFS; + if (!ascii_is_valid(string)) + return -EINVAL; - code = options[offset]; - len = options[++offset]; + free(*error_message); + *error_message = string; + string = NULL; + } - if (buflen < ++offset + len) + break; + case DHCP_OPTION_OVERLOAD: + if (len != 1) return -EINVAL; - if (cb) - cb(code, len, &options[offset], userdata); + if (overload) + *overload = *option; + + break; - offset += len; + default: + if (cb) + cb(code, len, option, userdata); break; } + + offset += len; } if (offset < buflen) @@ -213,8 +228,8 @@ static int parse_options(const uint8_t options[], size_t buflen, uint8_t *overlo return 0; } -int dhcp_option_parse(DHCPMessage *message, size_t len, - dhcp_option_cb_t cb, void *userdata) { +int dhcp_option_parse(DHCPMessage *message, size_t len, dhcp_option_cb_t cb, void *userdata, char **_error_message) { + _cleanup_free_ char *error_message = NULL; uint8_t overload = 0; uint8_t message_type = 0; int r; @@ -227,27 +242,29 @@ int dhcp_option_parse(DHCPMessage *message, size_t len, len -= sizeof(DHCPMessage); - r = parse_options(message->options, len, &overload, &message_type, - cb, userdata); + r = parse_options(message->options, len, &overload, &message_type, &error_message, cb, userdata); if (r < 0) return r; if (overload & DHCP_OVERLOAD_FILE) { - r = parse_options(message->file, sizeof(message->file), - NULL, &message_type, cb, userdata); + r = parse_options(message->file, sizeof(message->file), NULL, &message_type, &error_message, cb, userdata); if (r < 0) return r; } if (overload & DHCP_OVERLOAD_SNAME) { - r = parse_options(message->sname, sizeof(message->sname), - NULL, &message_type, cb, userdata); + r = parse_options(message->sname, sizeof(message->sname), NULL, &message_type, &error_message, cb, userdata); if (r < 0) return r; } - if (message_type) - return message_type; + if (message_type == 0) + return -ENOMSG; + + if (_error_message && IN_SET(message_type, DHCP_NAK, DHCP_DECLINE)) { + *_error_message = error_message; + error_message = NULL; + } - return -ENOMSG; + return message_type; } diff --git a/src/libsystemd-network/dhcp-packet.c b/src/libsystemd-network/dhcp-packet.c index cd7f5095ca..9ff42b155e 100644 --- a/src/libsystemd-network/dhcp-packet.c +++ b/src/libsystemd-network/dhcp-packet.c @@ -19,13 +19,12 @@ ***/ #include <errno.h> -#include <string.h> #include <net/ethernet.h> #include <net/if_arp.h> +#include <string.h> - -#include "dhcp-protocol.h" #include "dhcp-internal.h" +#include "dhcp-protocol.h" #define DHCP_CLIENT_MIN_OPTIONS_SIZE 312 diff --git a/src/libsystemd-network/dhcp-protocol.h b/src/libsystemd-network/dhcp-protocol.h index 88a81d2866..f65529a00e 100644 --- a/src/libsystemd-network/dhcp-protocol.h +++ b/src/libsystemd-network/dhcp-protocol.h @@ -21,8 +21,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <netinet/udp.h> #include <netinet/ip.h> +#include <netinet/udp.h> #include <stdint.h> #include "macro.h" @@ -132,11 +132,13 @@ enum { DHCP_OPTION_MESSAGE_TYPE = 53, DHCP_OPTION_SERVER_IDENTIFIER = 54, DHCP_OPTION_PARAMETER_REQUEST_LIST = 55, + DHCP_OPTION_ERROR_MESSAGE = 56, DHCP_OPTION_MAXIMUM_MESSAGE_SIZE = 57, DHCP_OPTION_RENEWAL_T1_TIME = 58, DHCP_OPTION_REBINDING_T2_TIME = 59, DHCP_OPTION_VENDOR_CLASS_IDENTIFIER = 60, DHCP_OPTION_CLIENT_IDENTIFIER = 61, + DHCP_OPTION_FQDN = 81, DHCP_OPTION_NEW_POSIX_TIMEZONE = 100, DHCP_OPTION_NEW_TZDB_TIMEZONE = 101, DHCP_OPTION_CLASSLESS_STATIC_ROUTE = 121, @@ -144,3 +146,12 @@ enum { DHCP_OPTION_PRIVATE_LAST = 254, DHCP_OPTION_END = 255, }; + +#define DHCP_MAX_FQDN_LENGTH 255 + +enum { + DHCP_FQDN_FLAG_S = (1 << 0), + DHCP_FQDN_FLAG_O = (1 << 1), + DHCP_FQDN_FLAG_E = (1 << 2), + DHCP_FQDN_FLAG_N = (1 << 3), +}; diff --git a/src/libsystemd-network/dhcp-server-internal.h b/src/libsystemd-network/dhcp-server-internal.h index 3b88b93d9a..a42f622c37 100644 --- a/src/libsystemd-network/dhcp-server-internal.h +++ b/src/libsystemd-network/dhcp-server-internal.h @@ -22,14 +22,13 @@ #pragma once -#include "sd-event.h" #include "sd-dhcp-server.h" +#include "sd-event.h" +#include "dhcp-internal.h" #include "hashmap.h" -#include "util.h" #include "log.h" - -#include "dhcp-internal.h" +#include "util.h" typedef struct DHCPClientId { size_t length; diff --git a/src/libsystemd-network/dhcp6-lease-internal.h b/src/libsystemd-network/dhcp6-lease-internal.h index 4edecf7711..f6cf0b30d3 100644 --- a/src/libsystemd-network/dhcp6-lease-internal.h +++ b/src/libsystemd-network/dhcp6-lease-internal.h @@ -25,6 +25,7 @@ #include <stdint.h> #include "sd-dhcp6-lease.h" + #include "dhcp6-internal.h" struct sd_dhcp6_lease { diff --git a/src/libsystemd-network/dhcp6-network.c b/src/libsystemd-network/dhcp6-network.c index 318ee9c4b4..fd2d60c9d5 100644 --- a/src/libsystemd-network/dhcp6-network.c +++ b/src/libsystemd-network/dhcp6-network.c @@ -33,36 +33,32 @@ #include "socket-util.h" int dhcp6_network_bind_udp_socket(int index, struct in6_addr *local_address) { - struct in6_pktinfo pktinfo = { - .ipi6_ifindex = index, - }; union sockaddr_union src = { .in6.sin6_family = AF_INET6, .in6.sin6_port = htobe16(DHCP6_PORT_CLIENT), - .in6.sin6_addr = IN6ADDR_ANY_INIT, + .in6.sin6_scope_id = index, }; _cleanup_close_ int s = -1; int r, off = 0, on = 1; - if (local_address) - memcpy(&src.in6.sin6_addr, local_address, - sizeof(src.in6.sin6_addr)); + assert(index > 0); + assert(local_address); + + src.in6.sin6_addr = *local_address; - s = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, - IPPROTO_UDP); + s = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, IPPROTO_UDP); if (s < 0) return -errno; - r = setsockopt(s, IPPROTO_IPV6, IPV6_PKTINFO, &pktinfo, - sizeof(pktinfo)); + r = setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)); if (r < 0) return -errno; - r = setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)); + r = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &off, sizeof(off)); if (r < 0) return -errno; - r = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &off, sizeof(off)); + r = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); if (r < 0) return -errno; diff --git a/src/libsystemd-network/dhcp6-option.c b/src/libsystemd-network/dhcp6-option.c index 0f46df6a1b..850212aea1 100644 --- a/src/libsystemd-network/dhcp6-option.c +++ b/src/libsystemd-network/dhcp6-option.c @@ -19,8 +19,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <netinet/in.h> #include <errno.h> +#include <netinet/in.h> #include <string.h> #include "alloc-util.h" @@ -360,7 +360,6 @@ int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, char * /* End of name */ break; else if (c <= 63) { - _cleanup_free_ char *t = NULL; const char *label; /* Literal label */ @@ -369,21 +368,20 @@ int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, char * if (pos > optlen) return -EMSGSIZE; - r = dns_label_escape(label, c, &t); - if (r < 0) - goto fail; - - if (!GREEDY_REALLOC0(ret, allocated, n + !first + strlen(t) + 1)) { + if (!GREEDY_REALLOC(ret, allocated, n + !first + DNS_LABEL_ESCAPED_MAX)) { r = -ENOMEM; goto fail; } - if (!first) - ret[n++] = '.'; - else + if (first) first = false; + else + ret[n++] = '.'; + + r = dns_label_escape(label, c, ret + n, DNS_LABEL_ESCAPED_MAX); + if (r < 0) + goto fail; - memcpy(ret + n, t, r); n += r; continue; } else { diff --git a/src/libsystemd-network/icmp6-util.c b/src/libsystemd-network/icmp6-util.c index 91308bf6c3..acad9d7d6a 100644 --- a/src/libsystemd-network/icmp6-util.c +++ b/src/libsystemd-network/icmp6-util.c @@ -47,17 +47,15 @@ int icmp6_bind_router_solicitation(int index) { .ipv6mr_interface = index, }; _cleanup_close_ int s = -1; - int r, zero = 0, hops = 255; + int r, zero = 0, one = 1, hops = 255; - s = socket(AF_INET6, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, - IPPROTO_ICMPV6); + s = socket(AF_INET6, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, IPPROTO_ICMPV6); if (s < 0) return -errno; ICMP6_FILTER_SETBLOCKALL(&filter); ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filter); - r = setsockopt(s, IPPROTO_ICMPV6, ICMP6_FILTER, &filter, - sizeof(filter)); + r = setsockopt(s, IPPROTO_ICMPV6, ICMP6_FILTER, &filter, sizeof(filter)); if (r < 0) return -errno; @@ -65,23 +63,23 @@ int icmp6_bind_router_solicitation(int index) { IPV6_PKTINFO socket option also applies for ICMPv6 multicast. Empirical experiments indicates otherwise and therefore an IPV6_MULTICAST_IF socket option is used here instead */ - r = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_IF, &index, - sizeof(index)); + r = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_IF, &index, sizeof(index)); if (r < 0) return -errno; - r = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &zero, - sizeof(zero)); + r = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &zero, sizeof(zero)); if (r < 0) return -errno; - r = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &hops, - sizeof(hops)); + r = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &hops, sizeof(hops)); if (r < 0) return -errno; - r = setsockopt(s, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq, - sizeof(mreq)); + r = setsockopt(s, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq, sizeof(mreq)); + if (r < 0) + return -errno; + + r = setsockopt(s, SOL_IPV6, IPV6_RECVHOPLIMIT, &one, sizeof(one)); if (r < 0) return -errno; @@ -101,25 +99,25 @@ int icmp6_send_router_solicitation(int s, const struct ether_addr *ether_addr) { struct ether_addr rs_opt_mac; } _packed_ rs = { .rs.nd_rs_type = ND_ROUTER_SOLICIT, + .rs_opt.nd_opt_type = ND_OPT_SOURCE_LINKADDR, + .rs_opt.nd_opt_len = 1, }; - struct iovec iov[1] = { - { &rs, }, + struct iovec iov = { + .iov_base = &rs, + .iov_len = sizeof(rs), }; struct msghdr msg = { .msg_name = &dst, .msg_namelen = sizeof(dst), - .msg_iov = iov, + .msg_iov = &iov, .msg_iovlen = 1, }; int r; - if (ether_addr) { - memcpy(&rs.rs_opt_mac, ether_addr, ETH_ALEN); - rs.rs_opt.nd_opt_type = ND_OPT_SOURCE_LINKADDR; - rs.rs_opt.nd_opt_len = 1; - iov[0].iov_len = sizeof(rs); - } else - iov[0].iov_len = sizeof(rs.rs); + assert(s >= 0); + assert(ether_addr); + + rs.rs_opt_mac = *ether_addr; r = sendmsg(s, &msg, 0); if (r < 0) diff --git a/src/libsystemd-network/lldp-port.c b/src/libsystemd-network/lldp-port.c index 97b6b485d2..1f1a49adbf 100644 --- a/src/libsystemd-network/lldp-port.c +++ b/src/libsystemd-network/lldp-port.c @@ -22,9 +22,9 @@ #include "alloc-util.h" #include "async.h" -#include "lldp-port.h" -#include "lldp-network.h" #include "lldp-internal.h" +#include "lldp-network.h" +#include "lldp-port.h" int lldp_port_start(lldp_port *p) { int r; diff --git a/src/libsystemd-network/lldp-tlv.c b/src/libsystemd-network/lldp-tlv.c index 7890160497..66343147a1 100644 --- a/src/libsystemd-network/lldp-tlv.c +++ b/src/libsystemd-network/lldp-tlv.c @@ -20,8 +20,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <net/ethernet.h> #include <arpa/inet.h> +#include <net/ethernet.h> #include "alloc-util.h" #include "lldp-tlv.h" diff --git a/src/libsystemd-network/lldp-tlv.h b/src/libsystemd-network/lldp-tlv.h index ca1da113d5..f5cd77477f 100644 --- a/src/libsystemd-network/lldp-tlv.h +++ b/src/libsystemd-network/lldp-tlv.h @@ -24,12 +24,12 @@ #include <net/ethernet.h> -#include "util.h" -#include "lldp.h" -#include "list.h" - #include "sd-lldp.h" +#include "list.h" +#include "lldp.h" +#include "util.h" + typedef struct sd_lldp_packet tlv_packet; typedef struct sd_lldp_section tlv_section; diff --git a/src/libsystemd-network/network-internal.c b/src/libsystemd-network/network-internal.c index 52d76e443e..a4d4f1ae2f 100644 --- a/src/libsystemd-network/network-internal.c +++ b/src/libsystemd-network/network-internal.c @@ -29,9 +29,9 @@ #include "condition.h" #include "conf-parser.h" #include "dhcp-lease-internal.h" +#include "hexdecoct.h" #include "log.h" #include "network-internal.h" -#include "hexdecoct.h" #include "parse-util.h" #include "siphash24.h" #include "string-util.h" @@ -56,7 +56,7 @@ const char *net_get_name(struct udev_device *device) { #define HASH_KEY SD_ID128_MAKE(d3,1e,48,fa,90,fe,4b,4c,9d,af,d5,d7,a1,b1,2e,8a) -int net_get_unique_predictable_data(struct udev_device *device, uint8_t result[8]) { +int net_get_unique_predictable_data(struct udev_device *device, uint64_t *result) { size_t l, sz = 0; const char *name = NULL; int r; @@ -81,7 +81,7 @@ int net_get_unique_predictable_data(struct udev_device *device, uint8_t result[8 /* Let's hash the machine ID plus the device name. We * use a fixed, but originally randomly created hash * key here. */ - siphash24(result, v, sz, HASH_KEY.bytes); + *result = htole64(siphash24(v, sz, HASH_KEY.bytes)); return 0; } diff --git a/src/libsystemd-network/network-internal.h b/src/libsystemd-network/network-internal.h index d5d4ef42f2..8a30921966 100644 --- a/src/libsystemd-network/network-internal.h +++ b/src/libsystemd-network/network-internal.h @@ -23,8 +23,8 @@ #include <stdbool.h> -#include "udev.h" #include "condition.h" +#include "udev.h" bool net_match_config(const struct ether_addr *match_mac, char * const *match_path, @@ -62,7 +62,7 @@ int config_parse_ifalias(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int net_get_unique_predictable_data(struct udev_device *device, uint8_t result[8]); +int net_get_unique_predictable_data(struct udev_device *device, uint64_t *result); const char *net_get_name(struct udev_device *device); void serialize_in_addrs(FILE *f, const struct in_addr *addresses, size_t size); diff --git a/src/libsystemd-network/sd-dhcp-client.c b/src/libsystemd-network/sd-dhcp-client.c index 137537253a..7deb00af9c 100644 --- a/src/libsystemd-network/sd-dhcp-client.c +++ b/src/libsystemd-network/sd-dhcp-client.c @@ -34,6 +34,8 @@ #include "dhcp-internal.h" #include "dhcp-lease-internal.h" #include "dhcp-protocol.h" +#include "dns-domain.h" +#include "hostname-util.h" #include "random-util.h" #include "string-util.h" #include "util.h" @@ -298,6 +300,9 @@ int sd_dhcp_client_set_hostname(sd_dhcp_client *client, assert_return(client, -EINVAL); + if (!hostname_is_valid(hostname, false) && !dns_name_is_valid(hostname)) + return -EINVAL; + if (streq_ptr(client->hostname, hostname)) return 0; @@ -539,6 +544,24 @@ static int client_message_init(sd_dhcp_client *client, DHCPPacket **ret, return 0; } +static int client_append_fqdn_option(DHCPMessage *message, size_t optlen, size_t *optoffset, + const char *fqdn) { + uint8_t buffer[3 + DHCP_MAX_FQDN_LENGTH]; + int r; + + buffer[0] = DHCP_FQDN_FLAG_S | /* Request server to perform A RR DNS updates */ + DHCP_FQDN_FLAG_E; /* Canonical wire format */ + buffer[1] = 0; /* RCODE1 (deprecated) */ + buffer[2] = 0; /* RCODE2 (deprecated) */ + + r = dns_name_to_wire_format(fqdn, buffer + 3, sizeof(buffer) - 3); + if (r > 0) + r = dhcp_option_append(message, optlen, optoffset, 0, + DHCP_OPTION_FQDN, 3 + r, buffer); + + return r; +} + static int dhcp_client_send_raw(sd_dhcp_client *client, DHCPPacket *packet, size_t len) { dhcp_packet_append_ip_headers(packet, INADDR_ANY, DHCP_PORT_CLIENT, @@ -576,13 +599,21 @@ static int client_send_discover(sd_dhcp_client *client) { return r; } - /* it is unclear from RFC 2131 if client should send hostname in - DHCPDISCOVER but dhclient does and so we do as well - */ if (client->hostname) { - r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0, - DHCP_OPTION_HOST_NAME, - strlen(client->hostname), client->hostname); + /* According to RFC 4702 "clients that send the Client FQDN option in + their messages MUST NOT also send the Host Name option". Just send + one of the two depending on the hostname type. + */ + if (dns_name_is_single_label(client->hostname)) { + /* it is unclear from RFC 2131 if client should send hostname in + DHCPDISCOVER but dhclient does and so we do as well + */ + r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0, + DHCP_OPTION_HOST_NAME, + strlen(client->hostname), client->hostname); + } else + r = client_append_fqdn_option(&discover->dhcp, optlen, &optoffset, + client->hostname); if (r < 0) return r; } @@ -688,9 +719,13 @@ static int client_send_request(sd_dhcp_client *client) { } if (client->hostname) { - r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0, - DHCP_OPTION_HOST_NAME, - strlen(client->hostname), client->hostname); + if (dns_name_is_single_label(client->hostname)) + r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0, + DHCP_OPTION_HOST_NAME, + strlen(client->hostname), client->hostname); + else + r = client_append_fqdn_option(&request->dhcp, optlen, &optoffset, + client->hostname); if (r < 0) return r; } @@ -1047,7 +1082,7 @@ static int client_handle_offer(sd_dhcp_client *client, DHCPMessage *offer, return r; } - r = dhcp_option_parse(offer, len, dhcp_lease_parse_options, lease); + r = dhcp_option_parse(offer, len, dhcp_lease_parse_options, lease, NULL); if (r != DHCP_OFFER) { log_dhcp_client(client, "received message was not an OFFER, ignoring"); return -ENOMSG; @@ -1086,7 +1121,7 @@ static int client_handle_forcerenew(sd_dhcp_client *client, DHCPMessage *force, size_t len) { int r; - r = dhcp_option_parse(force, len, NULL, NULL); + r = dhcp_option_parse(force, len, NULL, NULL, NULL); if (r != DHCP_FORCERENEW) return -ENOMSG; @@ -1098,6 +1133,7 @@ static int client_handle_forcerenew(sd_dhcp_client *client, DHCPMessage *force, static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack, size_t len) { _cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL; + _cleanup_free_ char *error_message = NULL; int r; r = dhcp_lease_new(&lease); @@ -1112,9 +1148,9 @@ static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack, return r; } - r = dhcp_option_parse(ack, len, dhcp_lease_parse_options, lease); + r = dhcp_option_parse(ack, len, dhcp_lease_parse_options, lease, &error_message); if (r == DHCP_NAK) { - log_dhcp_client(client, "NAK"); + log_dhcp_client(client, "NAK: %s", strna(error_message)); return -EADDRNOTAVAIL; } @@ -1478,9 +1514,8 @@ static int client_receive_message_udp(sd_event_source *s, int fd, r = ioctl(fd, FIONREAD, &buflen); if (r < 0) - return r; - - if (buflen < 0) + return -errno; + else if (buflen < 0) /* this can't be right */ return -EIO; @@ -1490,26 +1525,28 @@ static int client_receive_message_udp(sd_event_source *s, int fd, len = read(fd, message, buflen); if (len < 0) { - log_dhcp_client(client, "could not receive message from UDP " - "socket: %m"); - return 0; + if (errno == EAGAIN || errno == EINTR) + return 0; + + log_dhcp_client(client, "Could not receive message from UDP socket: %m"); + return -errno; } else if ((size_t)len < sizeof(DHCPMessage)) { - log_dhcp_client(client, "too small to be a DHCP message: ignoring"); + log_dhcp_client(client, "Too small to be a DHCP message: ignoring"); return 0; } if (be32toh(message->magic) != DHCP_MAGIC_COOKIE) { - log_dhcp_client(client, "not a DHCP message: ignoring"); + log_dhcp_client(client, "Not a DHCP message: ignoring"); return 0; } if (message->op != BOOTREPLY) { - log_dhcp_client(client, "not a BOOTREPLY message: ignoring"); + log_dhcp_client(client, "Not a BOOTREPLY message: ignoring"); return 0; } if (message->htype != client->arp_type) { - log_dhcp_client(client, "packet type does not match client type"); + log_dhcp_client(client, "Packet type does not match client type"); return 0; } @@ -1523,13 +1560,12 @@ static int client_receive_message_udp(sd_event_source *s, int fd, } if (message->hlen != expected_hlen) { - log_dhcp_client(client, "unexpected packet hlen %d", message->hlen); + log_dhcp_client(client, "Unexpected packet hlen %d", message->hlen); return 0; } if (memcmp(&message->chaddr[0], expected_chaddr, ETH_ALEN)) { - log_dhcp_client(client, "received chaddr does not match " - "expected: ignoring"); + log_dhcp_client(client, "Received chaddr does not match expected: ignoring"); return 0; } @@ -1537,8 +1573,7 @@ static int client_receive_message_udp(sd_event_source *s, int fd, be32toh(message->xid) != client->xid) { /* in BOUND state, we may receive FORCERENEW with xid set by server, so ignore the xid in this case */ - log_dhcp_client(client, "received xid (%u) does not match " - "expected (%u): ignoring", + log_dhcp_client(client, "Received xid (%u) does not match expected (%u): ignoring", be32toh(message->xid), client->xid); return 0; } @@ -1567,9 +1602,8 @@ static int client_receive_message_raw(sd_event_source *s, int fd, r = ioctl(fd, FIONREAD, &buflen); if (r < 0) - return r; - - if (buflen < 0) + return -errno; + else if (buflen < 0) /* this can't be right */ return -EIO; @@ -1582,9 +1616,12 @@ static int client_receive_message_raw(sd_event_source *s, int fd, len = recvmsg(fd, &msg, 0); if (len < 0) { - log_dhcp_client(client, "could not receive message from raw " - "socket: %m"); - return 0; + if (errno == EAGAIN || errno == EINTR) + return 0; + + log_dhcp_client(client, "Could not receive message from raw socket: %m"); + + return -errno; } else if ((size_t)len < sizeof(DHCPPacket)) return 0; diff --git a/src/libsystemd-network/sd-dhcp-lease.c b/src/libsystemd-network/sd-dhcp-lease.c index 8befedc500..fccdc01bc3 100644 --- a/src/libsystemd-network/sd-dhcp-lease.c +++ b/src/libsystemd-network/sd-dhcp-lease.c @@ -661,7 +661,7 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void break; default: - log_debug("Ignoring option DHCP option %i while parsing.", code); + log_debug("Ignoring option DHCP option %"PRIu8" while parsing.", code); break; } diff --git a/src/libsystemd-network/sd-dhcp-server.c b/src/libsystemd-network/sd-dhcp-server.c index 8d0d9955c3..587ff936ba 100644 --- a/src/libsystemd-network/sd-dhcp-server.c +++ b/src/libsystemd-network/sd-dhcp-server.c @@ -699,6 +699,7 @@ static int get_pool_offset(sd_dhcp_server *server, be32_t requested_ip) { int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, size_t length) { _cleanup_dhcp_request_free_ DHCPRequest *req = NULL; + _cleanup_free_ char *error_message = NULL; DHCPLease *existing_lease; int type, r; @@ -714,7 +715,7 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, if (!req) return -ENOMEM; - type = dhcp_option_parse(message, length, parse_request, req); + type = dhcp_option_parse(message, length, parse_request, req, &error_message); if (type < 0) return 0; @@ -753,7 +754,7 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, siphash24_init(&state, HASH_KEY.bytes); client_id_hash_func(&req->client_id, &state); - siphash24_finalize((uint8_t*)&hash, &state); + hash = htole64(siphash24_finalize(&state)); next_offer = hash % server->pool_size; for (i = 0; i < server->pool_size; i++) { @@ -784,8 +785,7 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, break; } case DHCP_DECLINE: - log_dhcp_server(server, "DECLINE (0x%x)", - be32toh(req->message->xid)); + log_dhcp_server(server, "DECLINE (0x%x): %s", be32toh(req->message->xid), strna(error_message)); /* TODO: make sure we don't offer this address again */ @@ -963,10 +963,10 @@ static int server_receive_message(sd_event_source *s, int fd, if (ioctl(fd, FIONREAD, &buflen) < 0) return -errno; - if (buflen < 0) + else if (buflen < 0) return -EIO; - message = malloc0(buflen); + message = malloc(buflen); if (!message) return -ENOMEM; @@ -974,9 +974,12 @@ static int server_receive_message(sd_event_source *s, int fd, iov.iov_len = buflen; len = recvmsg(fd, &msg, 0); - if (len < buflen) - return 0; - else if ((size_t)len < sizeof(DHCPMessage)) + if (len < 0) { + if (errno == EAGAIN || errno == EINTR) + return 0; + + return -errno; + } else if ((size_t)len < sizeof(DHCPMessage)) return 0; CMSG_FOREACH(cmsg, &msg) { diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index e29de60d24..36d909a4c5 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -32,6 +32,7 @@ #include "dhcp6-lease-internal.h" #include "dhcp6-protocol.h" #include "fd-util.h" +#include "in-addr-util.h" #include "network-internal.h" #include "random-util.h" #include "string-table.h" @@ -46,6 +47,7 @@ struct sd_dhcp6_client { sd_event *event; int event_priority; int index; + struct in6_addr local_address; uint8_t mac_addr[MAX_MAC_ADDR_LEN]; size_t mac_addr_len; uint16_t arp_type; @@ -133,6 +135,18 @@ int sd_dhcp6_client_set_index(sd_dhcp6_client *client, int interface_index) { return 0; } +int sd_dhcp6_client_set_local_address(sd_dhcp6_client *client, const struct in6_addr *local_address) { + assert_return(client, -EINVAL); + assert_return(local_address, -EINVAL); + assert_return(in_addr_is_link_local(AF_INET6, (const union in_addr_union *) local_address) > 0, -EINVAL); + + assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY); + + client->local_address = *local_address; + + return 0; +} + int sd_dhcp6_client_set_mac( sd_dhcp6_client *client, const uint8_t *addr, size_t addr_len, @@ -259,12 +273,12 @@ int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client, uint16_t option) int sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret) { assert_return(client, -EINVAL); - assert_return(ret, -EINVAL); if (!client->lease) return -ENOMSG; - *ret = client->lease; + if (ret) + *ret = client->lease; return 0; } @@ -881,7 +895,7 @@ static int client_receive_advertise(sd_dhcp6_client *client, DHCP6Message *adver static int client_receive_message(sd_event_source *s, int fd, uint32_t revents, void *userdata) { sd_dhcp6_client *client = userdata; DHCP6_CLIENT_DONT_DESTROY(client); - _cleanup_free_ DHCP6Message *message; + _cleanup_free_ DHCP6Message *message = NULL; int r, buflen, len; assert(s); @@ -889,18 +903,26 @@ static int client_receive_message(sd_event_source *s, int fd, uint32_t revents, assert(client->event); r = ioctl(fd, FIONREAD, &buflen); - if (r < 0 || buflen <= 0) - buflen = DHCP6_MIN_OPTIONS_SIZE; + if (r < 0) + return -errno; + else if (buflen < 0) + /* This really should not happen */ + return -EIO; - message = malloc0(buflen); + message = malloc(buflen); if (!message) return -ENOMEM; len = read(fd, message, buflen); - if ((size_t)len < sizeof(DHCP6Message)) { - log_dhcp6_client(client, "could not receive message from UDP socket: %m"); + if (len < 0) { + if (errno == EAGAIN || errno == EINTR) + return 0; + + log_dhcp6_client(client, "Could not receive message from UDP socket: %m"); + + return -errno; + } else if ((size_t)len < sizeof(DHCP6Message)) return 0; - } switch(message->type) { case DHCP6_SOLICIT: @@ -1115,11 +1137,19 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state) { } int sd_dhcp6_client_stop(sd_dhcp6_client *client) { + assert_return(client, -EINVAL); + client_stop(client, SD_DHCP6_CLIENT_EVENT_STOP); return 0; } +int sd_dhcp6_client_is_running(sd_dhcp6_client *client) { + assert_return(client, -EINVAL); + + return client->state != DHCP6_STATE_STOPPED; +} + int sd_dhcp6_client_start(sd_dhcp6_client *client) { int r = 0; enum DHCP6State state = DHCP6_STATE_SOLICITATION; @@ -1127,9 +1157,10 @@ int sd_dhcp6_client_start(sd_dhcp6_client *client) { assert_return(client, -EINVAL); assert_return(client->event, -EINVAL); assert_return(client->index > 0, -EINVAL); + assert_return(in_addr_is_link_local(AF_INET6, (const union in_addr_union *) &client->local_address) > 0, -EINVAL); if (!IN_SET(client->state, DHCP6_STATE_STOPPED)) - return -EALREADY; + return -EBUSY; r = client_reset(client); if (r < 0) @@ -1143,7 +1174,7 @@ int sd_dhcp6_client_start(sd_dhcp6_client *client) { if (r < 0) return r; - r = dhcp6_network_bind_udp_socket(client->index, NULL); + r = dhcp6_network_bind_udp_socket(client->index, &client->local_address); if (r < 0) return r; diff --git a/src/libsystemd-network/sd-ipv4ll.c b/src/libsystemd-network/sd-ipv4ll.c index 0d915e20e7..30a7ef5785 100644 --- a/src/libsystemd-network/sd-ipv4ll.c +++ b/src/libsystemd-network/sd-ipv4ll.c @@ -101,6 +101,8 @@ int sd_ipv4ll_new(sd_ipv4ll **ret) { if (!ll) return -ENOMEM; + ll->n_ref = 1; + r = sd_ipv4acd_new(&ll->acd); if (r < 0) return r; @@ -109,8 +111,6 @@ int sd_ipv4ll_new(sd_ipv4ll **ret) { if (r < 0) return r; - ll->n_ref = 1; - *ret = ll; ll = NULL; @@ -143,15 +143,14 @@ int sd_ipv4ll_set_mac(sd_ipv4ll *ll, const struct ether_addr *addr) { assert_return(ll, -EINVAL); if (!ll->random_data) { - uint8_t seed[8]; + uint64_t seed; /* If no random data is set, generate some from the MAC */ - siphash24(seed, &addr->ether_addr_octet, - ETH_ALEN, HASH_KEY.bytes); + seed = siphash24(&addr->ether_addr_octet, ETH_ALEN, HASH_KEY.bytes); assert_cc(sizeof(unsigned) <= 8); - r = sd_ipv4ll_set_address_seed(ll, *(unsigned*)seed); + r = sd_ipv4ll_set_address_seed(ll, (unsigned) htole64(seed)); if (r < 0) return r; } diff --git a/src/libsystemd-network/sd-ndisc.c b/src/libsystemd-network/sd-ndisc.c index c494b9d6d8..f2bce3b99f 100644 --- a/src/libsystemd-network/sd-ndisc.c +++ b/src/libsystemd-network/sd-ndisc.c @@ -29,8 +29,10 @@ #include "alloc-util.h" #include "async.h" #include "icmp6-util.h" +#include "in-addr-util.h" #include "list.h" #include "socket-util.h" +#include "string-util.h" #define NDISC_ROUTER_SOLICITATION_INTERVAL 4 * USEC_PER_SEC #define NDISC_MAX_ROUTER_SOLICITATIONS 3 @@ -47,6 +49,12 @@ enum NDiscState { #define ICMP6_RECV_SIZE (IP6_MIN_MTU - sizeof(struct ip6_hdr)) #define NDISC_OPT_LEN_UNITS 8 +#define ND_RA_FLAG_PREF 0x18 +#define ND_RA_FLAG_PREF_LOW 0x03 +#define ND_RA_FLAG_PREF_MEDIUM 0x0 +#define ND_RA_FLAG_PREF_HIGH 0x1 +#define ND_RA_FLAG_PREF_INVALID 0x2 + typedef struct NDiscPrefix NDiscPrefix; struct NDiscPrefix { @@ -75,6 +83,9 @@ struct sd_ndisc { sd_event_source *recv; sd_event_source *timeout; int nd_sent; + sd_ndisc_router_callback_t router_callback; + sd_ndisc_prefix_autonomous_callback_t prefix_autonomous_callback; + sd_ndisc_prefix_onlink_callback_t prefix_onlink_callback; sd_ndisc_callback_t callback; void *userdata; }; @@ -119,15 +130,17 @@ static int ndisc_prefix_new(sd_ndisc *nd, NDiscPrefix **ret) { return 0; } -static void ndisc_notify(sd_ndisc *nd, int event) { - if (nd->callback) - nd->callback(nd, event, nd->userdata); -} - -int sd_ndisc_set_callback(sd_ndisc *nd, sd_ndisc_callback_t callback, - void *userdata) { +int sd_ndisc_set_callback(sd_ndisc *nd, + sd_ndisc_router_callback_t router_callback, + sd_ndisc_prefix_onlink_callback_t prefix_onlink_callback, + sd_ndisc_prefix_autonomous_callback_t prefix_autonomous_callback, + sd_ndisc_callback_t callback, + void *userdata) { assert(nd); + nd->router_callback = router_callback; + nd->prefix_onlink_callback = prefix_onlink_callback; + nd->prefix_autonomous_callback = prefix_autonomous_callback; nd->callback = callback; nd->userdata = userdata; @@ -320,7 +333,7 @@ static int ndisc_prefix_match(sd_ndisc *nd, const struct in6_addr *addr, static int ndisc_prefix_update(sd_ndisc *nd, ssize_t len, const struct nd_opt_prefix_info *prefix_opt) { NDiscPrefix *prefix; - uint32_t lifetime; + uint32_t lifetime_valid, lifetime_preferred; usec_t time_now; char time_string[FORMAT_TIMESPAN_MAX]; int r; @@ -331,10 +344,17 @@ static int ndisc_prefix_update(sd_ndisc *nd, ssize_t len, if (len < prefix_opt->nd_opt_pi_len) return -ENOMSG; - if (!(prefix_opt->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_ONLINK)) + if (!(prefix_opt->nd_opt_pi_flags_reserved & (ND_OPT_PI_FLAG_ONLINK | ND_OPT_PI_FLAG_AUTO))) + return 0; + + if (in_addr_is_link_local(AF_INET6, (const union in_addr_union *) &prefix_opt->nd_opt_pi_prefix) > 0) return 0; - lifetime = be32toh(prefix_opt->nd_opt_pi_valid_time); + lifetime_valid = be32toh(prefix_opt->nd_opt_pi_valid_time); + lifetime_preferred = be32toh(prefix_opt->nd_opt_pi_preferred_time); + + if (lifetime_valid < lifetime_preferred) + return 0; r = ndisc_prefix_match(nd, &prefix_opt->nd_opt_pi_prefix, prefix_opt->nd_opt_pi_prefix_len, &prefix); @@ -357,8 +377,8 @@ static int ndisc_prefix_update(sd_ndisc *nd, ssize_t len, log_ndisc(nd, "New prefix "SD_NDISC_ADDRESS_FORMAT_STR"/%d lifetime %d expires in %s", SD_NDISC_ADDRESS_FORMAT_VAL(prefix->addr), - prefix->len, lifetime, - format_timespan(time_string, FORMAT_TIMESPAN_MAX, lifetime * USEC_PER_SEC, USEC_PER_SEC)); + prefix->len, lifetime_valid, + format_timespan(time_string, FORMAT_TIMESPAN_MAX, lifetime_valid * USEC_PER_SEC, USEC_PER_SEC)); LIST_PREPEND(prefixes, nd->prefixes, prefix); @@ -378,21 +398,27 @@ static int ndisc_prefix_update(sd_ndisc *nd, ssize_t len, log_ndisc(nd, "Update prefix "SD_NDISC_ADDRESS_FORMAT_STR"/%d lifetime %d expires in %s", SD_NDISC_ADDRESS_FORMAT_VAL(prefix->addr), - prefix->len, lifetime, - format_timespan(time_string, FORMAT_TIMESPAN_MAX, lifetime * USEC_PER_SEC, USEC_PER_SEC)); + prefix->len, lifetime_valid, + format_timespan(time_string, FORMAT_TIMESPAN_MAX, lifetime_valid * USEC_PER_SEC, USEC_PER_SEC)); } r = sd_event_now(nd->event, clock_boottime_or_monotonic(), &time_now); if (r < 0) return r; - prefix->valid_until = time_now + lifetime * USEC_PER_SEC; + prefix->valid_until = time_now + lifetime_valid * USEC_PER_SEC; - return r; + if ((prefix_opt->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_ONLINK) && nd->prefix_onlink_callback) + nd->prefix_onlink_callback(nd, &prefix->addr, prefix->len, prefix->valid_until, nd->userdata); + + if ((prefix_opt->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_AUTO) && nd->prefix_autonomous_callback) + nd->prefix_autonomous_callback(nd, &prefix->addr, prefix->len, lifetime_preferred, lifetime_valid, + nd->userdata); + + return 0; } -static int ndisc_ra_parse(sd_ndisc *nd, struct nd_router_advert *ra, - ssize_t len) { +static int ndisc_ra_parse(sd_ndisc *nd, struct nd_router_advert *ra, ssize_t len) { void *opt; struct nd_opt_hdr *opt_hdr; @@ -453,27 +479,88 @@ static int ndisc_ra_parse(sd_ndisc *nd, struct nd_router_advert *ra, } static int ndisc_router_advertisment_recv(sd_event_source *s, int fd, uint32_t revents, void *userdata) { + _cleanup_free_ struct nd_router_advert *ra = NULL; sd_ndisc *nd = userdata; - int r, buflen = 0; + union { + struct cmsghdr cmsghdr; + uint8_t buf[CMSG_LEN(sizeof(int))]; + } control = {}; + struct iovec iov = {}; + union sockaddr_union sa = {}; + struct msghdr msg = { + .msg_name = &sa.sa, + .msg_namelen = sizeof(sa), + .msg_iov = &iov, + .msg_iovlen = 1, + .msg_control = &control, + .msg_controllen = sizeof(control), + }; + struct cmsghdr *cmsg; + struct in6_addr *gw; + unsigned lifetime; ssize_t len; - _cleanup_free_ struct nd_router_advert *ra = NULL; - int event = SD_NDISC_EVENT_ROUTER_ADVERTISMENT_NONE; + int r, pref, stateful, buflen = 0; assert(s); assert(nd); assert(nd->event); r = ioctl(fd, FIONREAD, &buflen); - if (r < 0 || buflen <= 0) - buflen = ICMP6_RECV_SIZE; + if (r < 0) + return -errno; + else if (buflen < 0) + /* This really should not happen */ + return -EIO; + + iov.iov_len = buflen; - ra = malloc(buflen); + ra = malloc(iov.iov_len); if (!ra) return -ENOMEM; - len = read(fd, ra, buflen); + iov.iov_base = ra; + + len = recvmsg(fd, &msg, 0); if (len < 0) { - log_ndisc(nd, "Could not receive message from UDP socket: %m"); + if (errno == EAGAIN || errno == EINTR) + return 0; + + log_ndisc(nd, "Could not receive message from ICMPv6 socket: %m"); + return -errno; + } else if ((size_t)len < sizeof(struct nd_router_advert)) { + return 0; + } else if (msg.msg_namelen == 0) + gw = NULL; /* only happens when running the test-suite over a socketpair */ + else if (msg.msg_namelen != sizeof(sa.in6)) { + log_ndisc(nd, "Received invalid source address size from ICMPv6 socket: %zu bytes", (size_t)msg.msg_namelen); + return 0; + } else + gw = &sa.in6.sin6_addr; + + assert(!(msg.msg_flags & MSG_CTRUNC)); + assert(!(msg.msg_flags & MSG_TRUNC)); + + CMSG_FOREACH(cmsg, &msg) { + if (cmsg->cmsg_level == SOL_IPV6 && + cmsg->cmsg_type == IPV6_HOPLIMIT && + cmsg->cmsg_len == CMSG_LEN(sizeof(int))) { + int hops = *(int*)CMSG_DATA(cmsg); + + if (hops != 255) { + log_ndisc(nd, "Received RA with invalid hop limit %d. Ignoring.", hops); + return 0; + } + + break; + } + } + + if (gw && !in_addr_is_link_local(AF_INET6, (const union in_addr_union*) gw)) { + _cleanup_free_ char *addr = NULL; + + (void)in_addr_to_string(AF_INET6, (const union in_addr_union*) gw, &addr); + + log_ndisc(nd, "Received RA from non-link-local address %s. Ignoring.", strna(addr)); return 0; } @@ -487,26 +574,33 @@ static int ndisc_router_advertisment_recv(sd_event_source *s, int fd, uint32_t r nd->state = NDISC_STATE_ADVERTISMENT_LISTEN; - if (ra->nd_ra_flags_reserved & ND_RA_FLAG_OTHER ) - event = SD_NDISC_EVENT_ROUTER_ADVERTISMENT_OTHER; + stateful = ra->nd_ra_flags_reserved & (ND_RA_FLAG_MANAGED | ND_RA_FLAG_OTHER); + pref = (ra->nd_ra_flags_reserved & ND_RA_FLAG_PREF) >> 3; - if (ra->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED) - event = SD_NDISC_EVENT_ROUTER_ADVERTISMENT_MANAGED; + switch (pref) { + case ND_RA_FLAG_PREF_LOW: + case ND_RA_FLAG_PREF_HIGH: + break; + default: + pref = ND_RA_FLAG_PREF_MEDIUM; + break; + } - log_ndisc(nd, "Received Router Advertisement flags %s/%s", - ra->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED? "MANAGED": "none", - ra->nd_ra_flags_reserved & ND_RA_FLAG_OTHER? "OTHER": "none"); + lifetime = be16toh(ra->nd_ra_router_lifetime); - if (event != SD_NDISC_EVENT_ROUTER_ADVERTISMENT_NONE) { - r = ndisc_ra_parse(nd, ra, len); - if (r < 0) { - log_ndisc(nd, "Could not parse Router Advertisement: %s", - strerror(-r)); - return 0; - } + log_ndisc(nd, "Received Router Advertisement: flags %s preference %s lifetime %u sec", + stateful & ND_RA_FLAG_MANAGED ? "MANAGED" : stateful & ND_RA_FLAG_OTHER ? "OTHER" : "none", + pref == ND_RA_FLAG_PREF_HIGH ? "high" : pref == ND_RA_FLAG_PREF_LOW ? "low" : "medium", + lifetime); + + r = ndisc_ra_parse(nd, ra, len); + if (r < 0) { + log_ndisc(nd, "Could not parse Router Advertisement: %s", strerror(-r)); + return 0; } - ndisc_notify(nd, event); + if (nd->router_callback) + nd->router_callback(nd, stateful, gw, lifetime, pref, nd->userdata); return 0; } @@ -514,8 +608,6 @@ static int ndisc_router_advertisment_recv(sd_event_source *s, int fd, uint32_t r static int ndisc_router_solicitation_timeout(sd_event_source *s, uint64_t usec, void *userdata) { sd_ndisc *nd = userdata; uint64_t time_now, next_timeout; - struct ether_addr unset = { }; - struct ether_addr *addr = NULL; int r; assert(s); @@ -525,13 +617,11 @@ static int ndisc_router_solicitation_timeout(sd_event_source *s, uint64_t usec, nd->timeout = sd_event_source_unref(nd->timeout); if (nd->nd_sent >= NDISC_MAX_ROUTER_SOLICITATIONS) { - ndisc_notify(nd, SD_NDISC_EVENT_ROUTER_ADVERTISMENT_TIMEOUT); + if (nd->callback) + nd->callback(nd, SD_NDISC_EVENT_TIMEOUT, nd->userdata); nd->state = NDISC_STATE_ADVERTISMENT_LISTEN; } else { - if (memcmp(&nd->mac_addr, &unset, sizeof(struct ether_addr))) - addr = &nd->mac_addr; - - r = icmp6_send_router_solicitation(nd->fd, addr); + r = icmp6_send_router_solicitation(nd->fd, &nd->mac_addr); if (r < 0) log_ndisc(nd, "Error sending Router Solicitation"); else { @@ -549,7 +639,8 @@ static int ndisc_router_solicitation_timeout(sd_event_source *s, uint64_t usec, next_timeout, 0, ndisc_router_solicitation_timeout, nd); if (r < 0) { - ndisc_notify(nd, r); + /* we cannot continue if we are unable to rearm the timer */ + sd_ndisc_stop(nd); return 0; } @@ -575,6 +666,9 @@ int sd_ndisc_stop(sd_ndisc *nd) { nd->state = NDISC_STATE_IDLE; + if (nd->callback) + nd->callback(nd, SD_NDISC_EVENT_STOP, nd->userdata); + return 0; } @@ -585,7 +679,7 @@ int sd_ndisc_router_discovery_start(sd_ndisc *nd) { assert(nd->event); if (nd->state != NDISC_STATE_IDLE) - return -EINVAL; + return -EBUSY; if (nd->index < 1) return -EINVAL; diff --git a/src/libsystemd-network/test-acd.c b/src/libsystemd-network/test-acd.c index 94c31af3f3..69eff5116f 100644 --- a/src/libsystemd-network/test-acd.c +++ b/src/libsystemd-network/test-acd.c @@ -19,21 +19,21 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <stdlib.h> #include <errno.h> +#include <stdlib.h> #include <unistd.h> #include <linux/veth.h> #include <net/if.h> #include "sd-event.h" -#include "sd-netlink.h" #include "sd-ipv4acd.h" +#include "sd-netlink.h" -#include "util.h" #include "event-util.h" -#include "netlink-util.h" #include "in-addr-util.h" +#include "netlink-util.h" +#include "util.h" static void acd_handler(sd_ipv4acd *acd, int event, void *userdata) { assert_se(acd); diff --git a/src/libsystemd-network/test-dhcp-client.c b/src/libsystemd-network/test-dhcp-client.c index 5b52c1cbb9..4478147a83 100644 --- a/src/libsystemd-network/test-dhcp-client.c +++ b/src/libsystemd-network/test-dhcp-client.c @@ -32,8 +32,8 @@ #include "dhcp-internal.h" #include "dhcp-protocol.h" #include "event-util.h" -#include "util.h" #include "fd-util.h" +#include "util.h" static uint8_t mac_addr[] = {'A', 'B', 'C', '1', '2', '3'}; @@ -223,7 +223,7 @@ int dhcp_network_send_udp_socket(int s, be32_t address, uint16_t port, const voi static int test_discover_message_verify(size_t size, struct DHCPMessage *dhcp) { int res; - res = dhcp_option_parse(dhcp, size, check_options, NULL); + res = dhcp_option_parse(dhcp, size, check_options, NULL, NULL); assert_se(res == DHCP_DISCOVER); if (verbose) @@ -390,7 +390,7 @@ static int test_addr_acq_recv_request(size_t size, DHCPMessage *request) { uint8_t *msg_bytes = (uint8_t *)request; int res; - res = dhcp_option_parse(request, size, check_options, NULL); + res = dhcp_option_parse(request, size, check_options, NULL, NULL); assert_se(res == DHCP_REQUEST); assert_se(xid == request->xid); @@ -420,7 +420,7 @@ static int test_addr_acq_recv_discover(size_t size, DHCPMessage *discover) { uint8_t *msg_bytes = (uint8_t *)discover; int res; - res = dhcp_option_parse(discover, size, check_options, NULL); + res = dhcp_option_parse(discover, size, check_options, NULL, NULL); assert_se(res == DHCP_DISCOVER); assert_se(msg_bytes[size - 1] == DHCP_OPTION_END); diff --git a/src/libsystemd-network/test-dhcp-option.c b/src/libsystemd-network/test-dhcp-option.c index 2d29e28f16..75d22c4df3 100644 --- a/src/libsystemd-network/test-dhcp-option.c +++ b/src/libsystemd-network/test-dhcp-option.c @@ -1,8 +1,8 @@ /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ -#include <stdio.h> -#include <stdbool.h> #include <errno.h> +#include <stdbool.h> +#include <stdio.h> #include <string.h> #include "alloc-util.h" @@ -75,9 +75,8 @@ static const char *dhcp_type(int type) { static void test_invalid_buffer_length(void) { DHCPMessage message; - assert_se(dhcp_option_parse(&message, 0, NULL, NULL) == -EINVAL); - assert_se(dhcp_option_parse(&message, sizeof(DHCPMessage) - 1, NULL, NULL) - == -EINVAL); + assert_se(dhcp_option_parse(&message, 0, NULL, NULL, NULL) == -EINVAL); + assert_se(dhcp_option_parse(&message, sizeof(DHCPMessage) - 1, NULL, NULL, NULL) == -EINVAL); } static void test_message_init(void) { @@ -101,7 +100,7 @@ static void test_message_init(void) { assert_se(magic[2] == 83); assert_se(magic[3] == 99); - assert_se(dhcp_option_parse(message, len, NULL, NULL) >= 0); + assert_se(dhcp_option_parse(message, len, NULL, NULL, NULL) >= 0); } static DHCPMessage *create_message(uint8_t *options, uint16_t optlen, @@ -264,19 +263,12 @@ static void test_options(struct option_desc *desc) { buflen = sizeof(DHCPMessage) + optlen; if (!desc) { - assert_se((res = dhcp_option_parse(message, buflen, - test_options_cb, - NULL)) == -ENOMSG); + assert_se((res = dhcp_option_parse(message, buflen, test_options_cb, NULL, NULL)) == -ENOMSG); } else if (desc->success) { - assert_se((res = dhcp_option_parse(message, buflen, - test_options_cb, - desc)) >= 0); - assert_se(desc->pos == -1 && desc->filepos == -1 && - desc->snamepos == -1); + assert_se((res = dhcp_option_parse(message, buflen, test_options_cb, desc, NULL)) >= 0); + assert_se(desc->pos == -1 && desc->filepos == -1 && desc->snamepos == -1); } else - assert_se((res = dhcp_option_parse(message, buflen, - test_options_cb, - desc)) < 0); + assert_se((res = dhcp_option_parse(message, buflen, test_options_cb, desc, NULL)) < 0); if (verbose) printf("DHCP type %s\n", dhcp_type(res)); diff --git a/src/libsystemd-network/test-dhcp-server.c b/src/libsystemd-network/test-dhcp-server.c index 1a5c8c4605..2b5f59e4d6 100644 --- a/src/libsystemd-network/test-dhcp-server.c +++ b/src/libsystemd-network/test-dhcp-server.c @@ -200,13 +200,11 @@ static void test_message_handler(void) { static uint64_t client_id_hash_helper(DHCPClientId *id, uint8_t key[HASH_KEY_SIZE]) { struct siphash state; - uint64_t hash; siphash24_init(&state, key); client_id_hash_func(id, &state); - siphash24_finalize((uint8_t*)&hash, &state); - return hash; + return htole64(siphash24_finalize(&state)); } static void test_client_id_hash(void) { diff --git a/src/libsystemd-network/test-dhcp6-client.c b/src/libsystemd-network/test-dhcp6-client.c index 17ed6d58f3..9e05fde8f6 100644 --- a/src/libsystemd-network/test-dhcp6-client.c +++ b/src/libsystemd-network/test-dhcp6-client.c @@ -562,6 +562,7 @@ static void test_client_information_cb(sd_dhcp6_client *client, int event, sd_event *e = userdata; sd_dhcp6_lease *lease; struct in6_addr *addrs; + struct in6_addr address = { { { 0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01 } } }; char **domains; assert_se(e); @@ -590,6 +591,8 @@ static void test_client_information_cb(sd_dhcp6_client *client, int event, assert_se(sd_dhcp6_client_set_callback(client, test_client_solicit_cb, e) >= 0); + assert_se(sd_dhcp6_client_set_local_address(client, &address) >= 0); + assert_se(sd_dhcp6_client_start(client) >= 0); } @@ -701,6 +704,7 @@ int dhcp6_network_bind_udp_socket(int index, struct in6_addr *local_address) { static int test_client_solicit(sd_event *e) { sd_dhcp6_client *client; usec_t time_now = now(clock_boottime_or_monotonic()); + struct in6_addr address = { { { 0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01 } } }; int val = true; if (verbose) @@ -729,6 +733,8 @@ static int test_client_solicit(sd_event *e) { time_now + 2 * USEC_PER_SEC, 0, test_hangcheck, NULL) >= 0); + assert_se(sd_dhcp6_client_set_local_address(client, &address) >= 0); + assert_se(sd_dhcp6_client_start(client) >= 0); sd_event_loop(e); diff --git a/src/libsystemd-network/test-ndisc-rs.c b/src/libsystemd-network/test-ndisc-rs.c index 44eab94e8b..a485be704e 100644 --- a/src/libsystemd-network/test-ndisc-rs.c +++ b/src/libsystemd-network/test-ndisc-rs.c @@ -85,29 +85,28 @@ int icmp6_send_router_solicitation(int s, const struct ether_addr *ether_addr) { return send_ra_function(0); } -static void test_rs_done(sd_ndisc *nd, int event, void *userdata) { +static void test_rs_done(sd_ndisc *nd, uint8_t flags, const struct in6_addr *gateway, unsigned lifetime, int pref, void *userdata) { sd_event *e = userdata; - static int idx = 0; - struct { - uint8_t flag; - int event; - } flag_event[] = { - { 0, SD_NDISC_EVENT_ROUTER_ADVERTISMENT_NONE }, - { ND_RA_FLAG_OTHER, SD_NDISC_EVENT_ROUTER_ADVERTISMENT_OTHER }, - { ND_RA_FLAG_MANAGED, SD_NDISC_EVENT_ROUTER_ADVERTISMENT_MANAGED } + static unsigned idx = 0; + uint8_t flags_array[] = { + 0, + 0, + 0, + ND_RA_FLAG_OTHER, + ND_RA_FLAG_MANAGED }; uint32_t mtu; assert_se(nd); - assert_se(event == flag_event[idx].event); + assert_se(flags == flags_array[idx]); idx++; if (verbose) - printf(" got event %d\n", event); + printf(" got event 0x%02x\n", flags); - if (idx < 3) { - send_ra(flag_event[idx].flag); + if (idx < ELEMENTSOF(flags_array)) { + send_ra(flags_array[idx]); return; } @@ -135,7 +134,7 @@ static void test_rs(void) { assert_se(sd_ndisc_set_index(nd, 42) >= 0); assert_se(sd_ndisc_set_mac(nd, &mac_addr) >= 0); - assert_se(sd_ndisc_set_callback(nd, test_rs_done, e) >= 0); + assert_se(sd_ndisc_set_callback(nd, test_rs_done, NULL, NULL, NULL, e) >= 0); assert_se(sd_event_add_time(e, &test_hangcheck, clock_boottime_or_monotonic(), time_now + 2 *USEC_PER_SEC, 0, diff --git a/src/libsystemd/sd-bus/bus-bloom.c b/src/libsystemd/sd-bus/bus-bloom.c index 91fab90cb0..c0c5d445eb 100644 --- a/src/libsystemd/sd-bus/bus-bloom.c +++ b/src/libsystemd/sd-bus/bus-bloom.c @@ -19,9 +19,9 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include "util.h" -#include "siphash24.h" #include "bus-bloom.h" +#include "siphash24.h" +#include "util.h" static inline void set_bit(uint64_t filter[], unsigned long b) { filter[b >> 6] |= 1ULL << (b & 63); @@ -45,7 +45,7 @@ static void bloom_add_data( const void *data, /* Data to hash */ size_t n) { /* Size of data to hash in bytes */ - uint8_t h[8]; + uint64_t h; uint64_t m; unsigned w, i, c = 0; unsigned hash_index; @@ -72,11 +72,11 @@ static void bloom_add_data( for (d = 0; d < w; d++) { if (c <= 0) { - siphash24(h, data, n, hash_keys[hash_index++].bytes); + h = siphash24(data, n, hash_keys[hash_index++].bytes); c += 8; } - p = (p << 8ULL) | (uint64_t) h[8 - c]; + p = (p << 8ULL) | (uint64_t) ((uint8_t *)&h)[8 - c]; c--; } diff --git a/src/libsystemd/sd-bus/bus-bloom.h b/src/libsystemd/sd-bus/bus-bloom.h index a9350d7f51..38892044f1 100644 --- a/src/libsystemd/sd-bus/bus-bloom.h +++ b/src/libsystemd/sd-bus/bus-bloom.h @@ -22,6 +22,7 @@ ***/ #include <stdbool.h> +#include <stddef.h> #include <stdint.h> /* diff --git a/src/libsystemd/sd-bus/bus-common-errors.c b/src/libsystemd/sd-bus/bus-common-errors.c index 52f8dfd3be..8d486fcbbd 100644 --- a/src/libsystemd/sd-bus/bus-common-errors.c +++ b/src/libsystemd/sd-bus/bus-common-errors.c @@ -22,8 +22,9 @@ #include <errno.h> #include "sd-bus.h" -#include "bus-error.h" + #include "bus-common-errors.h" +#include "bus-error.h" BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_common_errors[] = { SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_UNIT, ENOENT), diff --git a/src/libsystemd/sd-bus/bus-container.c b/src/libsystemd/sd-bus/bus-container.c index 589a90bbff..7da6ba9903 100644 --- a/src/libsystemd/sd-bus/bus-container.c +++ b/src/libsystemd/sd-bus/bus-container.c @@ -19,8 +19,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <unistd.h> #include <fcntl.h> +#include <unistd.h> #include "bus-container.h" #include "bus-internal.h" diff --git a/src/libsystemd/sd-bus/bus-control.c b/src/libsystemd/sd-bus/bus-control.c index d5bc32e757..94251fe87c 100644 --- a/src/libsystemd/sd-bus/bus-control.c +++ b/src/libsystemd/sd-bus/bus-control.c @@ -23,8 +23,8 @@ #include <valgrind/memcheck.h> #endif -#include <stddef.h> #include <errno.h> +#include <stddef.h> #include "sd-bus.h" @@ -981,8 +981,12 @@ static int bus_get_owner_creds_kdbus(sd_bus *bus, uint64_t mask, sd_bus_creds ** static int bus_get_owner_creds_dbus1(sd_bus *bus, uint64_t mask, sd_bus_creds **ret) { _cleanup_bus_creds_unref_ sd_bus_creds *c = NULL; pid_t pid = 0; + bool do_label; int r; - bool do_label = bus->label && (mask & SD_BUS_CREDS_SELINUX_CONTEXT); + + assert(bus); + + do_label = bus->label && (mask & SD_BUS_CREDS_SELINUX_CONTEXT); /* Avoid allocating anything if we have no chance of returning useful data */ if (!bus->ucred_valid && !do_label) diff --git a/src/libsystemd/sd-bus/bus-control.h b/src/libsystemd/sd-bus/bus-control.h index 5009ca8e61..e01b075832 100644 --- a/src/libsystemd/sd-bus/bus-control.h +++ b/src/libsystemd/sd-bus/bus-control.h @@ -22,6 +22,7 @@ ***/ #include "sd-bus.h" + #include "bus-match.h" int bus_add_match_internal(sd_bus *bus, const char *match, struct bus_match_component *components, unsigned n_components, uint64_t cookie); diff --git a/src/libsystemd/sd-bus/bus-dump.h b/src/libsystemd/sd-bus/bus-dump.h index d2522edeba..71e56991fa 100644 --- a/src/libsystemd/sd-bus/bus-dump.h +++ b/src/libsystemd/sd-bus/bus-dump.h @@ -21,8 +21,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <stdio.h> #include <stdbool.h> +#include <stdio.h> #include "sd-bus.h" diff --git a/src/libsystemd/sd-bus/bus-error.c b/src/libsystemd/sd-bus/bus-error.c index 239d7245e6..404eaa3c89 100644 --- a/src/libsystemd/sd-bus/bus-error.c +++ b/src/libsystemd/sd-bus/bus-error.c @@ -29,10 +29,10 @@ #include "sd-bus.h" #include "alloc-util.h" +#include "bus-error.h" #include "errno-list.h" #include "string-util.h" #include "util.h" -#include "bus-error.h" BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_standard_errors[] = { SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.Failed", EACCES), diff --git a/src/libsystemd/sd-bus/bus-error.h b/src/libsystemd/sd-bus/bus-error.h index fb0199c948..d7fd8612d0 100644 --- a/src/libsystemd/sd-bus/bus-error.h +++ b/src/libsystemd/sd-bus/bus-error.h @@ -24,6 +24,7 @@ #include <stdbool.h> #include "sd-bus.h" + #include "macro.h" bool bus_error_is_dirty(sd_bus_error *e); diff --git a/src/libsystemd/sd-bus/bus-gvariant.c b/src/libsystemd/sd-bus/bus-gvariant.c index 402d43d66d..ec027590b2 100644 --- a/src/libsystemd/sd-bus/bus-gvariant.c +++ b/src/libsystemd/sd-bus/bus-gvariant.c @@ -19,9 +19,9 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include "bus-type.h" #include "bus-gvariant.h" #include "bus-signature.h" +#include "bus-type.h" int bus_gvariant_get_size(const char *signature) { const char *p; diff --git a/src/libsystemd/sd-bus/bus-introspect.h b/src/libsystemd/sd-bus/bus-introspect.h index 1914e6cb8b..57c2430ee8 100644 --- a/src/libsystemd/sd-bus/bus-introspect.h +++ b/src/libsystemd/sd-bus/bus-introspect.h @@ -24,6 +24,7 @@ #include <stdio.h> #include "sd-bus.h" + #include "set.h" struct introspect { diff --git a/src/libsystemd/sd-bus/bus-objects.c b/src/libsystemd/sd-bus/bus-objects.c index 8c472626a8..303e49fa84 100644 --- a/src/libsystemd/sd-bus/bus-objects.c +++ b/src/libsystemd/sd-bus/bus-objects.c @@ -23,6 +23,7 @@ #include "bus-internal.h" #include "bus-introspect.h" #include "bus-message.h" +#include "bus-objects.h" #include "bus-signature.h" #include "bus-slot.h" #include "bus-type.h" @@ -30,7 +31,6 @@ #include "set.h" #include "string-util.h" #include "strv.h" -#include "bus-objects.h" static int node_vtable_get_userdata( sd_bus *bus, diff --git a/src/libsystemd/sd-bus/bus-slot.c b/src/libsystemd/sd-bus/bus-slot.c index 550bad27ba..e405a04c53 100644 --- a/src/libsystemd/sd-bus/bus-slot.c +++ b/src/libsystemd/sd-bus/bus-slot.c @@ -24,8 +24,8 @@ #include "alloc-util.h" #include "bus-control.h" #include "bus-objects.h" -#include "string-util.h" #include "bus-slot.h" +#include "string-util.h" sd_bus_slot *bus_slot_allocate( sd_bus *bus, diff --git a/src/libsystemd/sd-bus/bus-slot.h b/src/libsystemd/sd-bus/bus-slot.h index 23a15e4d02..c997e58f9a 100644 --- a/src/libsystemd/sd-bus/bus-slot.h +++ b/src/libsystemd/sd-bus/bus-slot.h @@ -22,6 +22,7 @@ ***/ #include "sd-bus.h" + #include "bus-internal.h" sd_bus_slot *bus_slot_allocate(sd_bus *bus, bool floating, BusSlotType type, size_t extra, void *userdata); diff --git a/src/libsystemd/sd-bus/bus-socket.c b/src/libsystemd/sd-bus/bus-socket.c index a5cb667630..25873dea1e 100644 --- a/src/libsystemd/sd-bus/bus-socket.c +++ b/src/libsystemd/sd-bus/bus-socket.c @@ -36,6 +36,7 @@ #include "hexdecoct.h" #include "macro.h" #include "missing.h" +#include "selinux-util.h" #include "signal-util.h" #include "stdio-util.h" #include "string-util.h" @@ -608,9 +609,11 @@ static void bus_get_peercred(sd_bus *b) { b->ucred_valid = getpeercred(b->input_fd, &b->ucred) >= 0; /* Get the SELinux context of the peer */ - r = getpeersec(b->input_fd, &b->label); - if (r < 0 && r != -EOPNOTSUPP) - log_debug_errno(r, "Failed to determine peer security context: %m"); + if (mac_selinux_use()) { + r = getpeersec(b->input_fd, &b->label); + if (r < 0 && r != -EOPNOTSUPP) + log_debug_errno(r, "Failed to determine peer security context: %m"); + } } static int bus_socket_start_auth_client(sd_bus *b) { diff --git a/src/libsystemd/sd-bus/sd-bus.c b/src/libsystemd/sd-bus/sd-bus.c index a8d79b01b0..99780c8cce 100644 --- a/src/libsystemd/sd-bus/sd-bus.c +++ b/src/libsystemd/sd-bus/sd-bus.c @@ -20,12 +20,12 @@ ***/ #include <endian.h> -#include <stdlib.h> -#include <unistd.h> #include <netdb.h> #include <poll.h> -#include <sys/mman.h> #include <pthread.h> +#include <stdlib.h> +#include <sys/mman.h> +#include <unistd.h> #include "sd-bus.h" @@ -46,10 +46,10 @@ #include "cgroup-util.h" #include "def.h" #include "fd-util.h" +#include "hexdecoct.h" #include "hostname-util.h" #include "macro.h" #include "missing.h" -#include "hexdecoct.h" #include "parse-util.h" #include "string-util.h" #include "strv.h" diff --git a/src/libsystemd/sd-bus/test-bus-chat.c b/src/libsystemd/sd-bus/test-bus-chat.c index 767aef63ff..f20eced4ac 100644 --- a/src/libsystemd/sd-bus/test-bus-chat.c +++ b/src/libsystemd/sd-bus/test-bus-chat.c @@ -19,10 +19,10 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <stdlib.h> +#include <fcntl.h> #include <pthread.h> +#include <stdlib.h> #include <unistd.h> -#include <fcntl.h> #include "sd-bus.h" @@ -31,11 +31,11 @@ #include "bus-internal.h" #include "bus-match.h" #include "bus-util.h" +#include "fd-util.h" #include "formats-util.h" #include "log.h" #include "macro.h" #include "util.h" -#include "fd-util.h" static int match_callback(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) { log_info("Match triggered! interface=%s member=%s", strna(sd_bus_message_get_interface(m)), strna(sd_bus_message_get_member(m))); diff --git a/src/libsystemd/sd-bus/test-bus-creds.c b/src/libsystemd/sd-bus/test-bus-creds.c index 580117165a..bd0101af9e 100644 --- a/src/libsystemd/sd-bus/test-bus-creds.c +++ b/src/libsystemd/sd-bus/test-bus-creds.c @@ -20,6 +20,7 @@ ***/ #include "sd-bus.h" + #include "bus-dump.h" #include "bus-util.h" #include "cgroup-util.h" diff --git a/src/libsystemd/sd-bus/test-bus-error.c b/src/libsystemd/sd-bus/test-bus-error.c index 5753c04b0e..9d6c221eb0 100644 --- a/src/libsystemd/sd-bus/test-bus-error.c +++ b/src/libsystemd/sd-bus/test-bus-error.c @@ -20,10 +20,11 @@ ***/ #include "sd-bus.h" + +#include "bus-common-errors.h" #include "bus-error.h" #include "bus-util.h" #include "errno-list.h" -#include "bus-common-errors.h" static void test_error(void) { _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL, second = SD_BUS_ERROR_NULL; diff --git a/src/libsystemd/sd-bus/test-bus-introspect.c b/src/libsystemd/sd-bus/test-bus-introspect.c index f39dedeb24..26ba16d119 100644 --- a/src/libsystemd/sd-bus/test-bus-introspect.c +++ b/src/libsystemd/sd-bus/test-bus-introspect.c @@ -19,8 +19,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include "log.h" #include "bus-introspect.h" +#include "log.h" static int prop_get(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error) { return -EINVAL; diff --git a/src/libsystemd/sd-bus/test-bus-marshal.c b/src/libsystemd/sd-bus/test-bus-marshal.c index 0747d6a37c..0a6093e78b 100644 --- a/src/libsystemd/sd-bus/test-bus-marshal.c +++ b/src/libsystemd/sd-bus/test-bus-marshal.c @@ -19,8 +19,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <stdlib.h> #include <math.h> +#include <stdlib.h> #ifdef HAVE_GLIB #include <gio/gio.h> @@ -38,8 +38,8 @@ #include "bus-message.h" #include "bus-util.h" #include "fd-util.h" -#include "log.h" #include "hexdecoct.h" +#include "log.h" #include "util.h" static void test_bus_path_encode_unique(void) { diff --git a/src/libsystemd/sd-bus/test-bus-match.c b/src/libsystemd/sd-bus/test-bus-match.c index 75ea28371b..94896c196a 100644 --- a/src/libsystemd/sd-bus/test-bus-match.c +++ b/src/libsystemd/sd-bus/test-bus-match.c @@ -19,13 +19,12 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include "log.h" -#include "macro.h" - #include "bus-match.h" #include "bus-message.h" -#include "bus-util.h" #include "bus-slot.h" +#include "bus-util.h" +#include "log.h" +#include "macro.h" static bool mask[32]; diff --git a/src/libsystemd/sd-bus/test-bus-objects.c b/src/libsystemd/sd-bus/test-bus-objects.c index 5bc72e2355..edd63f9ea7 100644 --- a/src/libsystemd/sd-bus/test-bus-objects.c +++ b/src/libsystemd/sd-bus/test-bus-objects.c @@ -19,8 +19,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <stdlib.h> #include <pthread.h> +#include <stdlib.h> #include "sd-bus.h" diff --git a/src/libsystemd/sd-bus/test-bus-signature.c b/src/libsystemd/sd-bus/test-bus-signature.c index 92a810a7d8..949d16e6e9 100644 --- a/src/libsystemd/sd-bus/test-bus-signature.c +++ b/src/libsystemd/sd-bus/test-bus-signature.c @@ -19,10 +19,10 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include "string-util.h" -#include "log.h" -#include "bus-signature.h" #include "bus-internal.h" +#include "bus-signature.h" +#include "log.h" +#include "string-util.h" int main(int argc, char *argv[]) { char prefix[256]; diff --git a/src/libsystemd/sd-bus/test-bus-zero-copy.c b/src/libsystemd/sd-bus/test-bus-zero-copy.c index ff8df61a9e..1cf8416fa4 100644 --- a/src/libsystemd/sd-bus/test-bus-zero-copy.c +++ b/src/libsystemd/sd-bus/test-bus-zero-copy.c @@ -27,11 +27,11 @@ #include "bus-dump.h" #include "bus-kernel.h" #include "bus-message.h" +#include "fd-util.h" #include "log.h" #include "memfd-util.h" #include "string-util.h" #include "util.h" -#include "fd-util.h" #define FIRST_ARRAY 17 #define SECOND_ARRAY 33 diff --git a/src/libsystemd/sd-event/sd-event.c b/src/libsystemd/sd-event/sd-event.c index ee4886700e..3191b458d1 100644 --- a/src/libsystemd/sd-event/sd-event.c +++ b/src/libsystemd/sd-event/sd-event.c @@ -34,6 +34,7 @@ #include "macro.h" #include "missing.h" #include "prioq.h" +#include "process-util.h" #include "set.h" #include "signal-util.h" #include "string-util.h" @@ -415,11 +416,9 @@ _public_ int sd_event_new(sd_event** ret) { e->original_pid = getpid(); e->perturb = USEC_INFINITY; - e->pending = prioq_new(pending_prioq_compare); - if (!e->pending) { - r = -ENOMEM; + r = prioq_ensure_allocated(&e->pending, pending_prioq_compare); + if (r < 0) goto fail; - } e->epoll_fd = epoll_create1(EPOLL_CLOEXEC); if (e->epoll_fd < 0) { @@ -436,7 +435,9 @@ fail: } _public_ sd_event* sd_event_ref(sd_event *e) { - assert_return(e, NULL); + + if (!e) + return NULL; assert(e->n_ref >= 1); e->n_ref++; @@ -808,7 +809,7 @@ static void source_disconnect(sd_event_source *s) { s->event->n_enabled_child_sources--; } - (void) hashmap_remove(s->event->child_sources, INT_TO_PTR(s->child.pid)); + (void) hashmap_remove(s->event->child_sources, PID_TO_PTR(s->child.pid)); event_gc_signal_data(s->event, &s->priority, SIGCHLD); } @@ -1049,17 +1050,13 @@ _public_ int sd_event_add_time( d = event_get_clock_data(e, type); assert(d); - if (!d->earliest) { - d->earliest = prioq_new(earliest_time_prioq_compare); - if (!d->earliest) - return -ENOMEM; - } + r = prioq_ensure_allocated(&d->earliest, earliest_time_prioq_compare); + if (r < 0) + return r; - if (!d->latest) { - d->latest = prioq_new(latest_time_prioq_compare); - if (!d->latest) - return -ENOMEM; - } + r = prioq_ensure_allocated(&d->latest, latest_time_prioq_compare); + if (r < 0) + return r; if (d->fd < 0) { r = event_setup_timer_fd(e, d, clock); @@ -1188,7 +1185,7 @@ _public_ int sd_event_add_child( if (r < 0) return r; - if (hashmap_contains(e->child_sources, INT_TO_PTR(pid))) + if (hashmap_contains(e->child_sources, PID_TO_PTR(pid))) return -EBUSY; s = source_new(e, !ret, SOURCE_CHILD); @@ -1201,7 +1198,7 @@ _public_ int sd_event_add_child( s->userdata = userdata; s->enabled = SD_EVENT_ONESHOT; - r = hashmap_put(e->child_sources, INT_TO_PTR(pid), s); + r = hashmap_put(e->child_sources, PID_TO_PTR(pid), s); if (r < 0) { source_free(s); return r; @@ -1310,11 +1307,9 @@ _public_ int sd_event_add_exit( assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); assert_return(!event_pid_changed(e), -ECHILD); - if (!e->exit) { - e->exit = prioq_new(exit_prioq_compare); - if (!e->exit) - return -ENOMEM; - } + r = prioq_ensure_allocated(&e->exit, exit_prioq_compare); + if (r < 0) + return r; s = source_new(e, !ret, SOURCE_EXIT); if (!s) @@ -1338,7 +1333,9 @@ _public_ int sd_event_add_exit( } _public_ sd_event_source* sd_event_source_ref(sd_event_source *s) { - assert_return(s, NULL); + + if (!s) + return NULL; assert(s->n_ref >= 1); s->n_ref++; @@ -2432,7 +2429,9 @@ _public_ int sd_event_prepare(sd_event *e) { e->iteration++; + e->state = SD_EVENT_PREPARING; r = event_prepare(e); + e->state = SD_EVENT_INITIAL; if (r < 0) return r; diff --git a/src/libsystemd/sd-event/test-event.c b/src/libsystemd/sd-event/test-event.c index c1a3b49483..9417a8d1d1 100644 --- a/src/libsystemd/sd-event/test-event.c +++ b/src/libsystemd/sd-event/test-event.c @@ -158,11 +158,22 @@ static int exit_handler(sd_event_source *s, void *userdata) { return 3; } +static bool got_post = false; + +static int post_handler(sd_event_source *s, void *userdata) { + log_info("got post handler"); + + got_post = true; + + return 2; +} + static void test_basic(void) { sd_event *e = NULL; sd_event_source *w = NULL, *x = NULL, *y = NULL, *z = NULL, *q = NULL, *t = NULL; static const char ch = 'x'; int a[2] = { -1, -1 }, b[2] = { -1, -1}, d[2] = { -1, -1}, k[2] = { -1, -1 }; + uint64_t event_now; assert_se(pipe(a) >= 0); assert_se(pipe(b) >= 0); @@ -170,6 +181,7 @@ static void test_basic(void) { assert_se(pipe(k) >= 0); assert_se(sd_event_default(&e) >= 0); + assert_se(sd_event_now(e, CLOCK_MONOTONIC, &event_now) > 0); assert_se(sd_event_set_watchdog(e, true) >= 0); @@ -230,10 +242,14 @@ static void test_basic(void) { sd_event_source_unref(y); do_quit = true; - assert_se(sd_event_source_set_time(z, now(CLOCK_MONOTONIC) + 200 * USEC_PER_MSEC) >= 0); + assert_se(sd_event_add_post(e, NULL, post_handler, NULL) >= 0); + assert_se(sd_event_now(e, CLOCK_MONOTONIC, &event_now) == 0); + assert_se(sd_event_source_set_time(z, event_now + 200 * USEC_PER_MSEC) >= 0); assert_se(sd_event_source_set_enabled(z, SD_EVENT_ONESHOT) >= 0); assert_se(sd_event_loop(e) >= 0); + assert_se(got_post); + assert_se(got_exit); sd_event_source_unref(z); sd_event_source_unref(q); diff --git a/src/libsystemd/sd-id128/sd-id128.c b/src/libsystemd/sd-id128/sd-id128.c index 1e17ea6a06..c12bb1e20b 100644 --- a/src/libsystemd/sd-id128/sd-id128.c +++ b/src/libsystemd/sd-id128/sd-id128.c @@ -26,9 +26,9 @@ #include "sd-id128.h" #include "fd-util.h" +#include "hexdecoct.h" #include "io-util.h" #include "macro.h" -#include "hexdecoct.h" #include "random-util.h" #include "util.h" diff --git a/src/libsystemd/sd-netlink/local-addresses.c b/src/libsystemd/sd-netlink/local-addresses.c index a00865b56b..d431ba4be4 100644 --- a/src/libsystemd/sd-netlink/local-addresses.c +++ b/src/libsystemd/sd-netlink/local-addresses.c @@ -23,9 +23,9 @@ #include "sd-netlink.h" #include "alloc-util.h" -#include "netlink-util.h" -#include "macro.h" #include "local-addresses.h" +#include "macro.h" +#include "netlink-util.h" static int address_compare(const void *_a, const void *_b) { const struct local_address *a = _a, *b = _b; diff --git a/src/libsystemd/sd-netlink/local-addresses.h b/src/libsystemd/sd-netlink/local-addresses.h index 5d0f11a2c1..74d4f25534 100644 --- a/src/libsystemd/sd-netlink/local-addresses.h +++ b/src/libsystemd/sd-netlink/local-addresses.h @@ -23,6 +23,7 @@ #include "sd-netlink.h" + #include "in-addr-util.h" struct local_address { diff --git a/src/libsystemd/sd-netlink/netlink-types.c b/src/libsystemd/sd-netlink/netlink-types.c index cf0a6248d6..135354e5f3 100644 --- a/src/libsystemd/sd-netlink/netlink-types.c +++ b/src/libsystemd/sd-netlink/netlink-types.c @@ -83,20 +83,20 @@ static const NLTypeSystem empty_type_system = { .types = empty_types, }; -static const NLType rtnl_link_info_data_veth_types[VETH_INFO_MAX + 1] = { +static const NLType rtnl_link_info_data_veth_types[] = { [VETH_INFO_PEER] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) }, }; -static const NLType rtnl_link_info_data_ipvlan_types[IFLA_IPVLAN_MAX + 1] = { +static const NLType rtnl_link_info_data_ipvlan_types[] = { [IFLA_IPVLAN_MODE] = { .type = NETLINK_TYPE_U16 }, }; -static const NLType rtnl_link_info_data_macvlan_types[IFLA_MACVLAN_MAX + 1] = { +static const NLType rtnl_link_info_data_macvlan_types[] = { [IFLA_MACVLAN_MODE] = { .type = NETLINK_TYPE_U32 }, [IFLA_MACVLAN_FLAGS] = { .type = NETLINK_TYPE_U16 }, }; -static const NLType rtnl_link_bridge_management_types[IFLA_BRIDGE_MAX + 1] = { +static const NLType rtnl_link_bridge_management_types[] = { [IFLA_BRIDGE_FLAGS] = { .type = NETLINK_TYPE_U16 }, [IFLA_BRIDGE_MODE] = { .type = NETLINK_TYPE_U16 }, /* @@ -105,7 +105,7 @@ static const NLType rtnl_link_bridge_management_types[IFLA_BRIDGE_MAX + 1] = { */ }; -static const NLType rtnl_link_info_data_bridge_types[IFLA_BR_MAX + 1] = { +static const NLType rtnl_link_info_data_bridge_types[] = { [IFLA_BR_FORWARD_DELAY] = { .type = NETLINK_TYPE_U32 }, [IFLA_BR_HELLO_TIME] = { .type = NETLINK_TYPE_U32 }, [IFLA_BR_MAX_AGE] = { .type = NETLINK_TYPE_U32 }, @@ -114,7 +114,7 @@ static const NLType rtnl_link_info_data_bridge_types[IFLA_BR_MAX + 1] = { [IFLA_BR_PRIORITY] = { .type = NETLINK_TYPE_U16 }, }; -static const NLType rtnl_link_info_data_vlan_types[IFLA_VLAN_MAX + 1] = { +static const NLType rtnl_link_info_data_vlan_types[] = { [IFLA_VLAN_ID] = { .type = NETLINK_TYPE_U16 }, /* [IFLA_VLAN_FLAGS] = { .len = sizeof(struct ifla_vlan_flags) }, @@ -124,7 +124,7 @@ static const NLType rtnl_link_info_data_vlan_types[IFLA_VLAN_MAX + 1] = { [IFLA_VLAN_PROTOCOL] = { .type = NETLINK_TYPE_U16 }, }; -static const NLType rtnl_link_info_data_vxlan_types[IFLA_VXLAN_MAX+1] = { +static const NLType rtnl_link_info_data_vxlan_types[] = { [IFLA_VXLAN_ID] = { .type = NETLINK_TYPE_U32 }, [IFLA_VXLAN_GROUP] = { .type = NETLINK_TYPE_IN_ADDR }, [IFLA_VXLAN_LINK] = { .type = NETLINK_TYPE_U32 }, @@ -151,7 +151,7 @@ static const NLType rtnl_link_info_data_vxlan_types[IFLA_VXLAN_MAX+1] = { [IFLA_VXLAN_REMCSUM_NOPARTIAL] = { .type = NETLINK_TYPE_FLAG }, }; -static const NLType rtnl_bond_arp_target_types[BOND_ARP_TARGETS_MAX + 1] = { +static const NLType rtnl_bond_arp_target_types[] = { [BOND_ARP_TARGETS_0] = { .type = NETLINK_TYPE_U32 }, [BOND_ARP_TARGETS_1] = { .type = NETLINK_TYPE_U32 }, [BOND_ARP_TARGETS_2] = { .type = NETLINK_TYPE_U32 }, @@ -175,7 +175,7 @@ static const NLTypeSystem rtnl_bond_arp_type_system = { .types = rtnl_bond_arp_target_types, }; -static const NLType rtnl_link_info_data_bond_types[IFLA_BOND_MAX + 1] = { +static const NLType rtnl_link_info_data_bond_types[] = { [IFLA_BOND_MODE] = { .type = NETLINK_TYPE_U8 }, [IFLA_BOND_ACTIVE_SLAVE] = { .type = NETLINK_TYPE_U32 }, [IFLA_BOND_MIIMON] = { .type = NETLINK_TYPE_U32 }, @@ -201,7 +201,7 @@ static const NLType rtnl_link_info_data_bond_types[IFLA_BOND_MAX + 1] = { [IFLA_BOND_AD_INFO] = { .type = NETLINK_TYPE_NESTED }, }; -static const NLType rtnl_link_info_data_iptun_types[IFLA_IPTUN_MAX + 1] = { +static const NLType rtnl_link_info_data_iptun_types[] = { [IFLA_IPTUN_LINK] = { .type = NETLINK_TYPE_U32 }, [IFLA_IPTUN_LOCAL] = { .type = NETLINK_TYPE_IN_ADDR }, [IFLA_IPTUN_REMOTE] = { .type = NETLINK_TYPE_IN_ADDR }, @@ -220,7 +220,7 @@ static const NLType rtnl_link_info_data_iptun_types[IFLA_IPTUN_MAX + 1] = { [IFLA_IPTUN_ENCAP_DPORT] = { .type = NETLINK_TYPE_U16 }, }; -static const NLType rtnl_link_info_data_ipgre_types[IFLA_GRE_MAX + 1] = { +static const NLType rtnl_link_info_data_ipgre_types[] = { [IFLA_GRE_LINK] = { .type = NETLINK_TYPE_U32 }, [IFLA_GRE_IFLAGS] = { .type = NETLINK_TYPE_U16 }, [IFLA_GRE_OFLAGS] = { .type = NETLINK_TYPE_U16 }, @@ -239,7 +239,7 @@ static const NLType rtnl_link_info_data_ipgre_types[IFLA_GRE_MAX + 1] = { [IFLA_GRE_ENCAP_DPORT] = { .type = NETLINK_TYPE_U16 }, }; -static const NLType rtnl_link_info_data_ipvti_types[IFLA_VTI_MAX + 1] = { +static const NLType rtnl_link_info_data_ipvti_types[] = { [IFLA_VTI_LINK] = { .type = NETLINK_TYPE_U32 }, [IFLA_VTI_IKEY] = { .type = NETLINK_TYPE_U32 }, [IFLA_VTI_OKEY] = { .type = NETLINK_TYPE_U32 }, @@ -247,7 +247,7 @@ static const NLType rtnl_link_info_data_ipvti_types[IFLA_VTI_MAX + 1] = { [IFLA_VTI_REMOTE] = { .type = NETLINK_TYPE_IN_ADDR }, }; -static const NLType rtnl_link_info_data_ip6tnl_types[IFLA_IPTUN_MAX + 1] = { +static const NLType rtnl_link_info_data_ip6tnl_types[] = { [IFLA_IPTUN_LINK] = { .type = NETLINK_TYPE_U32 }, [IFLA_IPTUN_LOCAL] = { .type = NETLINK_TYPE_IN_ADDR }, [IFLA_IPTUN_REMOTE] = { .type = NETLINK_TYPE_IN_ADDR }, @@ -259,7 +259,7 @@ static const NLType rtnl_link_info_data_ip6tnl_types[IFLA_IPTUN_MAX + 1] = { }; /* these strings must match the .kind entries in the kernel */ -static const char* const nl_union_link_info_data_table[_NL_UNION_LINK_INFO_DATA_MAX] = { +static const char* const nl_union_link_info_data_table[] = { [NL_UNION_LINK_INFO_DATA_BOND] = "bond", [NL_UNION_LINK_INFO_DATA_BRIDGE] = "bridge", [NL_UNION_LINK_INFO_DATA_VLAN] = "vlan", @@ -282,7 +282,7 @@ static const char* const nl_union_link_info_data_table[_NL_UNION_LINK_INFO_DATA_ DEFINE_STRING_TABLE_LOOKUP(nl_union_link_info_data, NLUnionLinkInfoData); -static const NLTypeSystem rtnl_link_info_data_type_systems[_NL_UNION_LINK_INFO_DATA_MAX] = { +static const NLTypeSystem rtnl_link_info_data_type_systems[] = { [NL_UNION_LINK_INFO_DATA_BOND] = { .count = ELEMENTSOF(rtnl_link_info_data_bond_types), .types = rtnl_link_info_data_bond_types }, [NL_UNION_LINK_INFO_DATA_BRIDGE] = { .count = ELEMENTSOF(rtnl_link_info_data_bridge_types), @@ -328,7 +328,7 @@ static const NLTypeSystemUnion rtnl_link_info_data_type_system_union = { .match = IFLA_INFO_KIND, }; -static const NLType rtnl_link_info_types[IFLA_INFO_MAX + 1] = { +static const NLType rtnl_link_info_types[] = { [IFLA_INFO_KIND] = { .type = NETLINK_TYPE_STRING }, [IFLA_INFO_DATA] = { .type = NETLINK_TYPE_UNION, .type_system_union = &rtnl_link_info_data_type_system_union}, /* @@ -343,7 +343,7 @@ static const NLTypeSystem rtnl_link_info_type_system = { .types = rtnl_link_info_types, }; -static const struct NLType rtnl_prot_info_bridge_port_types[IFLA_BRPORT_MAX + 1] = { +static const struct NLType rtnl_prot_info_bridge_port_types[] = { [IFLA_BRPORT_STATE] = { .type = NETLINK_TYPE_U8 }, [IFLA_BRPORT_COST] = { .type = NETLINK_TYPE_U32 }, [IFLA_BRPORT_PRIORITY] = { .type = NETLINK_TYPE_U16 }, @@ -357,7 +357,7 @@ static const struct NLType rtnl_prot_info_bridge_port_types[IFLA_BRPORT_MAX + 1] [IFLA_BRPORT_LEARNING_SYNC] = { .type = NETLINK_TYPE_U8 }, }; -static const NLTypeSystem rtnl_prot_info_type_systems[AF_MAX] = { +static const NLTypeSystem rtnl_prot_info_type_systems[] = { [AF_BRIDGE] = { .count = ELEMENTSOF(rtnl_prot_info_bridge_port_types), .types = rtnl_prot_info_bridge_port_types }, }; @@ -368,7 +368,7 @@ static const NLTypeSystemUnion rtnl_prot_info_type_system_union = { .match_type = NL_MATCH_PROTOCOL, }; -static const struct NLType rtnl_af_spec_inet6_types[IFLA_INET6_MAX + 1] = { +static const struct NLType rtnl_af_spec_inet6_types[] = { [IFLA_INET6_FLAGS] = { .type = NETLINK_TYPE_U32 }, /* IFLA_INET6_CONF, @@ -386,7 +386,7 @@ static const NLTypeSystem rtnl_af_spec_inet6_type_system = { .types = rtnl_af_spec_inet6_types, }; -static const NLType rtnl_af_spec_types[AF_MAX + 1] = { +static const NLType rtnl_af_spec_types[] = { [AF_INET6] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_af_spec_inet6_type_system }, }; @@ -395,7 +395,7 @@ static const NLTypeSystem rtnl_af_spec_type_system = { .types = rtnl_af_spec_types, }; -static const NLType rtnl_link_types[IFLA_MAX + 1 ] = { +static const NLType rtnl_link_types[] = { [IFLA_ADDRESS] = { .type = NETLINK_TYPE_ETHER_ADDR }, [IFLA_BROADCAST] = { .type = NETLINK_TYPE_ETHER_ADDR }, [IFLA_IFNAME] = { .type = NETLINK_TYPE_STRING, .size = IFNAMSIZ - 1 }, @@ -454,7 +454,7 @@ static const NLTypeSystem rtnl_link_type_system = { /* IFA_FLAGS was defined in kernel 3.14, but we still support older * kernels where IFA_MAX is lower. */ -static const NLType rtnl_address_types[CONST_MAX(IFA_MAX, IFA_FLAGS) + 1] = { +static const NLType rtnl_address_types[] = { [IFA_ADDRESS] = { .type = NETLINK_TYPE_IN_ADDR }, [IFA_LOCAL] = { .type = NETLINK_TYPE_IN_ADDR }, [IFA_LABEL] = { .type = NETLINK_TYPE_STRING, .size = IFNAMSIZ - 1 }, @@ -472,7 +472,7 @@ static const NLTypeSystem rtnl_address_type_system = { .types = rtnl_address_types, }; -static const NLType rtnl_route_types[RTA_MAX + 1] = { +static const NLType rtnl_route_types[] = { [RTA_DST] = { .type = NETLINK_TYPE_IN_ADDR }, /* 6? */ [RTA_SRC] = { .type = NETLINK_TYPE_IN_ADDR }, /* 6? */ [RTA_IIF] = { .type = NETLINK_TYPE_U32 }, @@ -490,7 +490,11 @@ static const NLType rtnl_route_types[RTA_MAX + 1] = { RTA_TABLE, RTA_MARK, RTA_MFC_STATS, + RTA_VIA, + RTA_NEWDST, */ + [RTA_PREF] = { .type = NETLINK_TYPE_U8 }, + }; static const NLTypeSystem rtnl_route_type_system = { @@ -498,7 +502,7 @@ static const NLTypeSystem rtnl_route_type_system = { .types = rtnl_route_types, }; -static const NLType rtnl_neigh_types[NDA_MAX + 1] = { +static const NLType rtnl_neigh_types[] = { [NDA_DST] = { .type = NETLINK_TYPE_IN_ADDR }, [NDA_LLADDR] = { .type = NETLINK_TYPE_ETHER_ADDR }, [NDA_CACHEINFO] = { .type = NETLINK_TYPE_CACHE_INFO, .size = sizeof(struct nda_cacheinfo) }, @@ -514,7 +518,7 @@ static const NLTypeSystem rtnl_neigh_type_system = { .types = rtnl_neigh_types, }; -static const NLType rtnl_types[RTM_MAX + 1] = { +static const NLType rtnl_types[] = { [NLMSG_DONE] = { .type = NETLINK_TYPE_NESTED, .type_system = &empty_type_system, .size = 0 }, [NLMSG_ERROR] = { .type = NETLINK_TYPE_NESTED, .type_system = &empty_type_system, .size = sizeof(struct nlmsgerr) }, [RTM_NEWLINK] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) }, diff --git a/src/libsystemd/sd-netlink/netlink-types.h b/src/libsystemd/sd-netlink/netlink-types.h index bf7c641541..9e636a0b53 100644 --- a/src/libsystemd/sd-netlink/netlink-types.h +++ b/src/libsystemd/sd-netlink/netlink-types.h @@ -21,6 +21,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include "macro.h" + enum { NETLINK_TYPE_UNSPEC, NETLINK_TYPE_U8, /* NLA_U8 */ diff --git a/src/libsystemd/sd-netlink/netlink-util.c b/src/libsystemd/sd-netlink/netlink-util.c index 6f9fd2993b..95690b7ff1 100644 --- a/src/libsystemd/sd-netlink/netlink-util.c +++ b/src/libsystemd/sd-netlink/netlink-util.c @@ -21,8 +21,8 @@ #include "sd-netlink.h" -#include "netlink-util.h" #include "netlink-internal.h" +#include "netlink-util.h" int rtnl_set_link_name(sd_netlink **rtnl, int ifindex, const char *name) { _cleanup_netlink_message_unref_ sd_netlink_message *message = NULL; diff --git a/src/libsystemd/sd-netlink/rtnl-message.c b/src/libsystemd/sd-netlink/rtnl-message.c index 7cccb9b1d5..3e605db661 100644 --- a/src/libsystemd/sd-netlink/rtnl-message.c +++ b/src/libsystemd/sd-netlink/rtnl-message.c @@ -84,6 +84,35 @@ int sd_rtnl_message_route_set_scope(sd_netlink_message *m, unsigned char scope) return 0; } +int sd_rtnl_message_route_set_flags(sd_netlink_message *m, unsigned flags) { + struct rtmsg *rtm; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_route(m->hdr->nlmsg_type), -EINVAL); + + rtm = NLMSG_DATA(m->hdr); + + rtm->rtm_flags = flags; + + return 0; +} + +int sd_rtnl_message_route_get_flags(sd_netlink_message *m, unsigned *flags) { + struct rtmsg *rtm; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_route(m->hdr->nlmsg_type), -EINVAL); + assert_return(flags, -EINVAL); + + rtm = NLMSG_DATA(m->hdr); + + *flags = rtm->rtm_flags; + + return 0; +} + int sd_rtnl_message_route_get_family(sd_netlink_message *m, int *family) { struct rtmsg *rtm; diff --git a/src/libsystemd/sd-netlink/test-local-addresses.c b/src/libsystemd/sd-netlink/test-local-addresses.c index 7180175970..0b53297ab8 100644 --- a/src/libsystemd/sd-netlink/test-local-addresses.c +++ b/src/libsystemd/sd-netlink/test-local-addresses.c @@ -19,10 +19,10 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include "af-list.h" #include "alloc-util.h" #include "in-addr-util.h" #include "local-addresses.h" -#include "af-list.h" static void print_local_addresses(struct local_address *a, unsigned n) { unsigned i; diff --git a/src/libsystemd/sd-utf8/sd-utf8.c b/src/libsystemd/sd-utf8/sd-utf8.c index 381397cc52..9e52db3b3d 100644 --- a/src/libsystemd/sd-utf8/sd-utf8.c +++ b/src/libsystemd/sd-utf8/sd-utf8.c @@ -21,8 +21,8 @@ #include "sd-utf8.h" -#include "util.h" #include "utf8.h" +#include "util.h" _public_ const char *sd_utf8_is_valid(const char *s) { assert_return(s, NULL); diff --git a/src/libudev/libudev-private.h b/src/libudev/libudev-private.h index 5f50496291..52c5075110 100644 --- a/src/libudev/libudev-private.h +++ b/src/libudev/libudev-private.h @@ -21,8 +21,8 @@ #define _LIBUDEV_PRIVATE_H_ #include <signal.h> -#include <stdint.h> #include <stdbool.h> +#include <stdint.h> #include "libudev.h" diff --git a/src/libudev/libudev-queue.c b/src/libudev/libudev-queue.c index 58410b1b8f..e3dffa6925 100644 --- a/src/libudev/libudev-queue.c +++ b/src/libudev/libudev-queue.c @@ -18,11 +18,11 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <stdlib.h> -#include <stddef.h> -#include <unistd.h> #include <errno.h> +#include <stddef.h> +#include <stdlib.h> #include <sys/inotify.h> +#include <unistd.h> #include "alloc-util.h" #include "fd-util.h" diff --git a/src/login/logind-acl.h b/src/login/logind-acl.h index 93e9ed02eb..1f55759798 100644 --- a/src/login/logind-acl.h +++ b/src/login/logind-acl.h @@ -21,8 +21,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <sys/types.h> #include <stdbool.h> +#include <sys/types.h> #include "libudev.h" diff --git a/src/login/logind-action.h b/src/login/logind-action.h index e9b424b5f6..63c279cde7 100644 --- a/src/login/logind-action.h +++ b/src/login/logind-action.h @@ -35,8 +35,8 @@ typedef enum HandleAction { _HANDLE_ACTION_INVALID = -1 } HandleAction; -#include "logind.h" #include "logind-inhibit.h" +#include "logind.h" int manager_handle_action( Manager *m, diff --git a/src/login/logind-core.c b/src/login/logind-core.c index b3f30c8dc9..36cdbbe0f9 100644 --- a/src/login/logind-core.c +++ b/src/login/logind-core.c @@ -19,10 +19,10 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <sys/types.h> -#include <sys/ioctl.h> #include <fcntl.h> #include <pwd.h> +#include <sys/ioctl.h> +#include <sys/types.h> #include <linux/vt.h> #include "alloc-util.h" @@ -98,15 +98,16 @@ int manager_add_session(Manager *m, const char *id, Session **_session) { int manager_add_user(Manager *m, uid_t uid, gid_t gid, const char *name, User **_user) { User *u; + int r; assert(m); assert(name); u = hashmap_get(m->users, UID_TO_PTR(uid)); if (!u) { - u = user_new(m, uid, gid, name); - if (!u) - return -ENOMEM; + r = user_new(&u, m, uid, gid, name); + if (r < 0) + return r; } if (_user) diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c index 7890d68aa0..e507a19aef 100644 --- a/src/login/logind-dbus.c +++ b/src/login/logind-dbus.c @@ -308,8 +308,10 @@ static int method_get_session_by_pid(sd_bus_message *message, void *userdata, sd r = sd_bus_message_read(message, "u", &pid); if (r < 0) return r; + if (pid < 0) + return -EINVAL; - if (pid <= 0) { + if (pid == 0) { r = manager_get_session_from_creds(m, message, NULL, error, &session); if (r < 0) return r; @@ -369,8 +371,10 @@ static int method_get_user_by_pid(sd_bus_message *message, void *userdata, sd_bu r = sd_bus_message_read(message, "u", &pid); if (r < 0) return r; + if (pid < 0) + return -EINVAL; - if (pid <= 0) { + if (pid == 0) { r = manager_get_user_from_creds(m, message, UID_INVALID, error, &user); if (r < 0) return r; @@ -573,12 +577,14 @@ static int method_list_inhibitors(sd_bus_message *message, void *userdata, sd_bu static int method_create_session(sd_bus_message *message, void *userdata, sd_bus_error *error) { const char *service, *type, *class, *cseat, *tty, *display, *remote_user, *remote_host, *desktop; - uint32_t uid, leader, audit_id = 0; + uint32_t audit_id = 0; _cleanup_free_ char *id = NULL; Session *session = NULL; Manager *m = userdata; User *user = NULL; Seat *seat = NULL; + pid_t leader; + uid_t uid; int remote; uint32_t vtnr = 0; SessionType t; @@ -588,11 +594,16 @@ static int method_create_session(sd_bus_message *message, void *userdata, sd_bus assert(message); assert(m); + assert_cc(sizeof(pid_t) == sizeof(uint32_t)); + assert_cc(sizeof(uid_t) == sizeof(uint32_t)); + r = sd_bus_message_read(message, "uusssssussbss", &uid, &leader, &service, &type, &class, &desktop, &cseat, &vtnr, &tty, &display, &remote, &remote_user, &remote_host); if (r < 0) return r; - if (leader == 1) + if (!uid_is_valid(uid)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid UID"); + if (leader < 0 || leader == 1) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid leader PID"); if (isempty(type)) @@ -684,7 +695,7 @@ static int method_create_session(sd_bus_message *message, void *userdata, sd_bus c = SESSION_USER; } - if (leader <= 0) { + if (leader == 0) { _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL; r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_PID, &creds); @@ -1093,7 +1104,9 @@ static int method_set_user_linger(sd_bus_message *message, void *userdata, sd_bu r = sd_bus_creds_get_owner_uid(creds, &uid); if (r < 0) return r; - } + + } else if (!uid_is_valid(uid)) + return -EINVAL; errno = 0; pw = getpwuid(uid); @@ -2607,11 +2620,8 @@ int match_job_removed(sd_bus_message *message, void *userdata, sd_bus_error *err } session = hashmap_get(m->session_units, unit); - if (session) { - - if (streq_ptr(path, session->scope_job)) - session->scope_job = mfree(session->scope_job); - + if (session && streq_ptr(path, session->scope_job)) { + session->scope_job = mfree(session->scope_job); session_jobs_reply(session, unit, result); session_save(session); @@ -2620,7 +2630,9 @@ int match_job_removed(sd_bus_message *message, void *userdata, sd_bus_error *err } user = hashmap_get(m->user_units, unit); - if (user) { + if (user && + (streq_ptr(path, user->service_job) || + streq_ptr(path, user->slice_job))) { if (streq_ptr(path, user->service_job)) user->service_job = mfree(user->service_job); @@ -2740,13 +2752,101 @@ int manager_send_changed(Manager *manager, const char *property, ...) { l); } +int manager_start_slice( + Manager *manager, + const char *slice, + const char *description, + const char *after, + const char *after2, + uint64_t tasks_max, + sd_bus_error *error, + char **job) { + + _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL; + int r; + + assert(manager); + assert(slice); + + r = sd_bus_message_new_method_call( + manager->bus, + &m, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "StartTransientUnit"); + if (r < 0) + return r; + + r = sd_bus_message_append(m, "ss", strempty(slice), "fail"); + if (r < 0) + return r; + + r = sd_bus_message_open_container(m, 'a', "(sv)"); + if (r < 0) + return r; + + if (!isempty(description)) { + r = sd_bus_message_append(m, "(sv)", "Description", "s", description); + if (r < 0) + return r; + } + + if (!isempty(after)) { + r = sd_bus_message_append(m, "(sv)", "After", "as", 1, after); + if (r < 0) + return r; + } + + if (!isempty(after2)) { + r = sd_bus_message_append(m, "(sv)", "After", "as", 1, after2); + if (r < 0) + return r; + } + + r = sd_bus_message_append(m, "(sv)", "TasksMax", "t", tasks_max); + if (r < 0) + return r; + + r = sd_bus_message_close_container(m); + if (r < 0) + return r; + + r = sd_bus_message_append(m, "a(sa(sv))", 0); + if (r < 0) + return r; + + r = sd_bus_call(manager->bus, m, 0, error, &reply); + if (r < 0) + return r; + + if (job) { + const char *j; + char *copy; + + r = sd_bus_message_read(reply, "o", &j); + if (r < 0) + return r; + + copy = strdup(j); + if (!copy) + return -ENOMEM; + + *job = copy; + } + + return 1; +} + int manager_start_scope( Manager *manager, const char *scope, pid_t pid, const char *slice, const char *description, - const char *after, const char *after2, + const char *after, + const char *after2, + uint64_t tasks_max, sd_bus_error *error, char **job) { @@ -2814,6 +2914,10 @@ int manager_start_scope( if (r < 0) return r; + r = sd_bus_message_append(m, "(sv)", "TasksMax", "t", tasks_max); + if (r < 0) + return r; + r = sd_bus_message_close_container(m); if (r < 0) return r; @@ -2859,7 +2963,7 @@ int manager_start_unit(Manager *manager, const char *unit, sd_bus_error *error, "StartUnit", error, &reply, - "ss", unit, "fail"); + "ss", unit, "replace"); if (r < 0) return r; diff --git a/src/login/logind-gperf.gperf b/src/login/logind-gperf.gperf index 9218d098e0..8552c464cc 100644 --- a/src/login/logind-gperf.gperf +++ b/src/login/logind-gperf.gperf @@ -34,3 +34,4 @@ Login.IdleAction, config_parse_handle_action, 0, offsetof(Manag Login.IdleActionSec, config_parse_sec, 0, offsetof(Manager, idle_action_usec) Login.RuntimeDirectorySize, config_parse_tmpfs_size, 0, offsetof(Manager, runtime_dir_size) Login.RemoveIPC, config_parse_bool, 0, offsetof(Manager, remove_ipc) +Login.UserTasksMax, config_parse_uint64, 0, offsetof(Manager, user_tasks_max) diff --git a/src/login/logind-session.c b/src/login/logind-session.c index 1d561a6f8a..9f03a7b31e 100644 --- a/src/login/logind-session.c +++ b/src/login/logind-session.c @@ -36,7 +36,6 @@ #include "bus-util.h" #include "escape.h" #include "fd-util.h" -#include "fd-util.h" #include "fileio.h" #include "formats-util.h" #include "io-util.h" @@ -513,25 +512,31 @@ static int session_start_scope(Session *s) { assert(s); assert(s->user); - assert(s->user->slice); if (!s->scope) { _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; - _cleanup_free_ char *description = NULL; char *scope, *job = NULL; - - description = strjoin("Session ", s->id, " of user ", s->user->name, NULL); - if (!description) - return log_oom(); + const char *description; scope = strjoin("session-", s->id, ".scope", NULL); if (!scope) return log_oom(); - r = manager_start_scope(s->manager, scope, s->leader, s->user->slice, description, "systemd-logind.service", "systemd-user-sessions.service", &error, &job); + description = strjoina("Session ", s->id, " of user ", s->user->name, NULL); + + r = manager_start_scope( + s->manager, + scope, + s->leader, + s->user->slice, + description, + "systemd-logind.service", + "systemd-user-sessions.service", + (uint64_t) -1, /* disable TasksMax= for the scope, rely on the slice setting for it */ + &error, + &job); if (r < 0) { - log_error("Failed to start session scope %s: %s %s", - scope, bus_error_message(&error, r), error.name); + log_error_errno(r, "Failed to start session scope %s: %s", scope, bus_error_message(&error, r)); free(scope); return r; } else { @@ -543,7 +548,7 @@ static int session_start_scope(Session *s) { } if (s->scope) - hashmap_put(s->manager->session_units, s->scope, s); + (void) hashmap_put(s->manager->session_units, s->scope, s); return 0; } diff --git a/src/login/logind-session.h b/src/login/logind-session.h index d054c33cec..d27407fc92 100644 --- a/src/login/logind-session.h +++ b/src/login/logind-session.h @@ -25,8 +25,8 @@ typedef struct Session Session; typedef enum KillWho KillWho; #include "list.h" -#include "logind-user.h" #include "login-util.h" +#include "logind-user.h" typedef enum SessionState { SESSION_OPENING, /* Session scope is being created */ diff --git a/src/login/logind-user.c b/src/login/logind-user.c index 56bc5a010c..778f19b50d 100644 --- a/src/login/logind-user.c +++ b/src/login/logind-user.c @@ -25,6 +25,7 @@ #include <unistd.h> #include "alloc-util.h" +#include "bus-common-errors.h" #include "bus-error.h" #include "bus-util.h" #include "clean-ipc.h" @@ -44,47 +45,68 @@ #include "rm-rf.h" #include "smack-util.h" #include "special.h" +#include "stdio-util.h" #include "string-table.h" #include "unit-name.h" #include "user-util.h" #include "util.h" -User* user_new(Manager *m, uid_t uid, gid_t gid, const char *name) { - User *u; +int user_new(User **out, Manager *m, uid_t uid, gid_t gid, const char *name) { + _cleanup_(user_freep) User *u = NULL; + char lu[DECIMAL_STR_MAX(uid_t) + 1]; + int r; + assert(out); assert(m); assert(name); u = new0(User, 1); if (!u) - return NULL; + return -ENOMEM; + + u->manager = m; + u->uid = uid; + u->gid = gid; + xsprintf(lu, UID_FMT, uid); u->name = strdup(name); if (!u->name) - goto fail; + return -ENOMEM; if (asprintf(&u->state_file, "/run/systemd/users/"UID_FMT, uid) < 0) - goto fail; + return -ENOMEM; - if (hashmap_put(m->users, UID_TO_PTR(uid), u) < 0) - goto fail; + if (asprintf(&u->runtime_path, "/run/user/"UID_FMT, uid) < 0) + return -ENOMEM; - u->manager = m; - u->uid = uid; - u->gid = gid; + r = slice_build_subslice(SPECIAL_USER_SLICE, lu, &u->slice); + if (r < 0) + return r; - return u; + r = unit_name_build("user", lu, ".service", &u->service); + if (r < 0) + return r; -fail: - free(u->state_file); - free(u->name); - free(u); + r = hashmap_put(m->users, UID_TO_PTR(uid), u); + if (r < 0) + return r; + + r = hashmap_put(m->user_units, u->slice, u); + if (r < 0) + return r; - return NULL; + r = hashmap_put(m->user_units, u->service, u); + if (r < 0) + return r; + + *out = u; + u = NULL; + return 0; } -void user_free(User *u) { - assert(u); +User *user_free(User *u) { + if (!u) + return NULL; if (u->in_gc_queue) LIST_REMOVE(gc_queue, u->manager->user_gc_queue, u); @@ -92,26 +114,24 @@ void user_free(User *u) { while (u->sessions) session_free(u->sessions); - if (u->slice) { - hashmap_remove(u->manager->user_units, u->slice); - free(u->slice); - } + if (u->service) + hashmap_remove_value(u->manager->user_units, u->service, u); - if (u->service) { - hashmap_remove(u->manager->user_units, u->service); - free(u->service); - } + if (u->slice) + hashmap_remove_value(u->manager->user_units, u->slice, u); - free(u->slice_job); - free(u->service_job); + hashmap_remove_value(u->manager->users, UID_TO_PTR(u->uid), u); - free(u->runtime_path); + u->slice_job = mfree(u->slice_job); + u->service_job = mfree(u->service_job); - hashmap_remove(u->manager->users, UID_TO_PTR(u->uid)); + u->service = mfree(u->service); + u->slice = mfree(u->slice); + u->runtime_path = mfree(u->runtime_path); + u->state_file = mfree(u->state_file); + u->name = mfree(u->name); - free(u->name); - free(u->state_file); - free(u); + return mfree(u); } static int user_save_internal(User *u) { @@ -139,16 +159,13 @@ static int user_save_internal(User *u) { u->name, user_state_to_string(user_get_state(u))); + /* LEGACY: no-one reads RUNTIME= anymore, drop it at some point */ if (u->runtime_path) fprintf(f, "RUNTIME=%s\n", u->runtime_path); - if (u->service) - fprintf(f, "SERVICE=%s\n", u->service); if (u->service_job) fprintf(f, "SERVICE_JOB=%s\n", u->service_job); - if (u->slice) - fprintf(f, "SLICE=%s\n", u->slice); if (u->slice_job) fprintf(f, "SLICE_JOB=%s\n", u->slice_job); @@ -286,10 +303,7 @@ int user_load(User *u) { assert(u); r = parse_env_file(u->state_file, NEWLINE, - "RUNTIME", &u->runtime_path, - "SERVICE", &u->service, "SERVICE_JOB", &u->service_job, - "SLICE", &u->slice, "SLICE_JOB", &u->slice_job, "DISPLAY", &display, "REALTIME", &realtime, @@ -325,7 +339,6 @@ int user_load(User *u) { } static int user_mkdir_runtime_path(User *u) { - char *p; int r; assert(u); @@ -334,16 +347,10 @@ static int user_mkdir_runtime_path(User *u) { if (r < 0) return log_error_errno(r, "Failed to create /run/user: %m"); - if (!u->runtime_path) { - if (asprintf(&p, "/run/user/" UID_FMT, u->uid) < 0) - return log_oom(); - } else - p = u->runtime_path; - - if (path_is_mount_point(p, 0) <= 0) { + if (path_is_mount_point(u->runtime_path, 0) <= 0) { _cleanup_free_ char *t = NULL; - (void) mkdir_label(p, 0700); + (void) mkdir_label(u->runtime_path, 0700); if (mac_smack_use()) r = asprintf(&t, "mode=0700,smackfsroot=*,uid=" UID_FMT ",gid=" GID_FMT ",size=%zu", u->uid, u->gid, u->manager->runtime_dir_size); @@ -354,10 +361,10 @@ static int user_mkdir_runtime_path(User *u) { goto fail; } - r = mount("tmpfs", p, "tmpfs", MS_NODEV|MS_NOSUID, t); + r = mount("tmpfs", u->runtime_path, "tmpfs", MS_NODEV|MS_NOSUID, t); if (r < 0) { if (errno != EPERM) { - r = log_error_errno(errno, "Failed to mount per-user tmpfs directory %s: %m", p); + r = log_error_errno(errno, "Failed to mount per-user tmpfs directory %s: %m", u->runtime_path); goto fail; } @@ -365,62 +372,54 @@ static int user_mkdir_runtime_path(User *u) { * CAP_SYS_ADMIN-less container? In this case, * just use a normal directory. */ - r = chmod_and_chown(p, 0700, u->uid, u->gid); + r = chmod_and_chown(u->runtime_path, 0700, u->uid, u->gid); if (r < 0) { log_error_errno(r, "Failed to change runtime directory ownership and mode: %m"); goto fail; } } - r = label_fix(p, false, false); + r = label_fix(u->runtime_path, false, false); if (r < 0) - log_warning_errno(r, "Failed to fix label of '%s', ignoring: %m", p); + log_warning_errno(r, "Failed to fix label of '%s', ignoring: %m", u->runtime_path); } - u->runtime_path = p; return 0; fail: - if (p) { - /* Try to clean up, but ignore errors */ - (void) rmdir(p); - free(p); - } - - u->runtime_path = NULL; + /* Try to clean up, but ignore errors */ + (void) rmdir(u->runtime_path); return r; } static int user_start_slice(User *u) { + _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; + const char *description; char *job; int r; assert(u); - if (!u->slice) { - _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; - char lu[DECIMAL_STR_MAX(uid_t) + 1], *slice; - sprintf(lu, UID_FMT, u->uid); - - r = slice_build_subslice(SPECIAL_USER_SLICE, lu, &slice); - if (r < 0) - return r; - - r = manager_start_unit(u->manager, slice, &error, &job); - if (r < 0) { - log_error("Failed to start user slice: %s", bus_error_message(&error, r)); - free(slice); - } else { - u->slice = slice; - - free(u->slice_job); - u->slice_job = job; - } + u->slice_job = mfree(u->slice_job); + description = strjoina("User Slice of ", u->name); + + r = manager_start_slice( + u->manager, + u->slice, + description, + "systemd-logind.service", + "systemd-user-sessions.service", + u->manager->user_tasks_max, + &error, + &job); + if (r < 0) { + /* we don't fail due to this, let's try to continue */ + if (!sd_bus_error_has_name(&error, BUS_ERROR_UNIT_EXISTS)) + log_error_errno(r, "Failed to start user slice %s, ignoring: %s (%s)", u->slice, bus_error_message(&error, r), error.name); + } else { + u->slice_job = job; } - if (u->slice) - hashmap_put(u->manager->user_units, u->slice, u); - return 0; } @@ -431,29 +430,20 @@ static int user_start_service(User *u) { assert(u); - if (!u->service) { - char lu[DECIMAL_STR_MAX(uid_t) + 1], *service; - sprintf(lu, UID_FMT, u->uid); - - r = unit_name_build("user", lu, ".service", &service); - if (r < 0) - return log_error_errno(r, "Failed to build service name: %m"); - - r = manager_start_unit(u->manager, service, &error, &job); - if (r < 0) { - log_error("Failed to start user service: %s", bus_error_message(&error, r)); - free(service); - } else { - u->service = service; + u->service_job = mfree(u->service_job); - free(u->service_job); - u->service_job = job; - } + r = manager_start_unit( + u->manager, + u->service, + &error, + &job); + if (r < 0) { + /* we don't fail due to this, let's try to continue */ + log_error_errno(r, "Failed to start user service, ignoring: %s", bus_error_message(&error, r)); + } else { + u->service_job = job; } - if (u->service) - hashmap_put(u->manager->user_units, u->service, u); - return 0; } @@ -462,15 +452,32 @@ int user_start(User *u) { assert(u); - if (u->started) + if (u->started && !u->stopping) return 0; - log_debug("New user %s logged in.", u->name); - - /* Make XDG_RUNTIME_DIR */ - r = user_mkdir_runtime_path(u); - if (r < 0) - return r; + /* + * If u->stopping is set, the user is marked for removal and the slice + * and service stop-jobs are queued. We have to clear that flag before + * queing the start-jobs again. If they succeed, the user object can be + * re-used just fine (pid1 takes care of job-ordering and proper + * restart), but if they fail, we want to force another user_stop() so + * possibly pending units are stopped. + * Note that we don't clear u->started, as we have no clue what state + * the user is in on failure here. Hence, we pretend the user is + * running so it will be properly taken down by GC. However, we clearly + * return an error from user_start() in that case, so no further + * reference to the user is taken. + */ + u->stopping = false; + + if (!u->started) { + log_debug("New user %s logged in.", u->name); + + /* Make XDG_RUNTIME_DIR */ + r = user_mkdir_runtime_path(u); + if (r < 0) + return r; + } /* Create cgroup */ r = user_start_slice(u); @@ -488,16 +495,16 @@ int user_start(User *u) { if (r < 0) return r; - if (!dual_timestamp_is_set(&u->timestamp)) - dual_timestamp_get(&u->timestamp); - - u->started = true; + if (!u->started) { + if (!dual_timestamp_is_set(&u->timestamp)) + dual_timestamp_get(&u->timestamp); + user_send_signal(u, true); + u->started = true; + } /* Save new user data */ user_save(u); - user_send_signal(u, true); - return 0; } @@ -508,9 +515,6 @@ static int user_stop_slice(User *u) { assert(u); - if (!u->slice) - return 0; - r = manager_stop_unit(u->manager, u->slice, &error, &job); if (r < 0) { log_error("Failed to stop user slice: %s", bus_error_message(&error, r)); @@ -530,9 +534,6 @@ static int user_stop_service(User *u) { assert(u); - if (!u->service) - return 0; - r = manager_stop_unit(u->manager, u->service, &error, &job); if (r < 0) { log_error("Failed to stop user service: %s", bus_error_message(&error, r)); @@ -550,9 +551,6 @@ static int user_remove_runtime_path(User *u) { assert(u); - if (!u->runtime_path) - return 0; - r = rm_rf(u->runtime_path, 0); if (r < 0) log_error_errno(r, "Failed to remove runtime directory %s: %m", u->runtime_path); @@ -568,8 +566,6 @@ static int user_remove_runtime_path(User *u) { if (r < 0) log_error_errno(r, "Failed to remove runtime directory %s: %m", u->runtime_path); - u->runtime_path = mfree(u->runtime_path); - return r; } @@ -761,9 +757,6 @@ UserState user_get_state(User *u) { int user_kill(User *u, int signo) { assert(u); - if (!u->slice) - return -ESRCH; - return manager_kill_unit(u->manager, u->slice, KILL_ALL, signo, NULL); } diff --git a/src/login/logind-user.h b/src/login/logind-user.h index 722247806b..de99cf47b4 100644 --- a/src/login/logind-user.h +++ b/src/login/logind-user.h @@ -39,16 +39,13 @@ typedef enum UserState { struct User { Manager *manager; - uid_t uid; gid_t gid; char *name; - char *state_file; char *runtime_path; - - char *service; char *slice; + char *service; char *service_job; char *slice_job; @@ -65,8 +62,11 @@ struct User { LIST_FIELDS(User, gc_queue); }; -User* user_new(Manager *m, uid_t uid, gid_t gid, const char *name); -void user_free(User *u); +int user_new(User **out, Manager *m, uid_t uid, gid_t gid, const char *name); +User *user_free(User *u); + +DEFINE_TRIVIAL_CLEANUP_FUNC(User *, user_free); + bool user_check_gc(User *u, bool drop_not_started); void user_add_to_gc_queue(User *u); int user_start(User *u); diff --git a/src/login/logind-utmp.c b/src/login/logind-utmp.c index 3e7a935a34..3bd61a81fd 100644 --- a/src/login/logind-utmp.c +++ b/src/login/logind-utmp.c @@ -20,9 +20,9 @@ ***/ #include <errno.h> +#include <pwd.h> #include <string.h> #include <unistd.h> -#include <pwd.h> #include "sd-messages.h" diff --git a/src/login/logind.c b/src/login/logind.c index be6bbe5b5c..7b41174c64 100644 --- a/src/login/logind.c +++ b/src/login/logind.c @@ -70,6 +70,7 @@ static Manager *manager_new(void) { m->idle_action_not_before_usec = now(CLOCK_MONOTONIC); m->runtime_dir_size = PAGE_ALIGN((size_t) (physical_memory() / 10)); /* 10% */ + m->user_tasks_max = UINT64_C(4096); m->devices = hashmap_new(&string_hash_ops); m->seats = hashmap_new(&string_hash_ops); diff --git a/src/login/logind.conf b/src/login/logind.conf index 6df6f04c77..81f6695434 100644 --- a/src/login/logind.conf +++ b/src/login/logind.conf @@ -32,3 +32,4 @@ #IdleActionSec=30min #RuntimeDirectorySize=10% #RemoveIPC=yes +#UserTasksMax=4096 diff --git a/src/login/logind.h b/src/login/logind.h index 44e05d8b01..f34544e64c 100644 --- a/src/login/logind.h +++ b/src/login/logind.h @@ -134,6 +134,7 @@ struct Manager { sd_event_source *lid_switch_ignore_event_source; size_t runtime_dir_size; + uint64_t user_tasks_max; }; int manager_add_device(Manager *m, const char *sysfs, bool master, Device **_device); @@ -171,7 +172,8 @@ int bus_manager_shutdown_or_sleep_now_or_later(Manager *m, const char *unit_name int manager_send_changed(Manager *manager, const char *property, ...) _sentinel_; -int manager_start_scope(Manager *manager, const char *scope, pid_t pid, const char *slice, const char *description, const char *after, const char *after2, sd_bus_error *error, char **job); +int manager_start_slice(Manager *manager, const char *slice, const char *description, const char *after, const char *after2, uint64_t tasks_max, sd_bus_error *error, char **job); +int manager_start_scope(Manager *manager, const char *scope, pid_t pid, const char *slice, const char *description, const char *after, const char *after2, uint64_t tasks_max, sd_bus_error *error, char **job); int manager_start_unit(Manager *manager, const char *unit, sd_bus_error *error, char **job); int manager_stop_unit(Manager *manager, const char *unit, sd_bus_error *error, char **job); int manager_abandon_scope(Manager *manager, const char *scope, sd_bus_error *error); diff --git a/src/login/pam_systemd.c b/src/login/pam_systemd.c index 0d61f528db..ed4f7c726f 100644 --- a/src/login/pam_systemd.c +++ b/src/login/pam_systemd.c @@ -267,29 +267,21 @@ _public_ PAM_EXTERN int pam_sm_open_session( pam_get_item(handle, PAM_SERVICE, (const void**) &service); if (streq_ptr(service, "systemd-user")) { - _cleanup_free_ char *p = NULL, *rt = NULL; + _cleanup_free_ char *rt = NULL; - if (asprintf(&p, "/run/systemd/users/"UID_FMT, pw->pw_uid) < 0) + if (asprintf(&rt, "/run/user/"UID_FMT, pw->pw_uid) < 0) return PAM_BUF_ERR; - r = parse_env_file(p, NEWLINE, - "RUNTIME", &rt, - NULL); - if (r < 0 && r != -ENOENT) - return PAM_SESSION_ERR; - - if (rt) { - r = pam_misc_setenv(handle, "XDG_RUNTIME_DIR", rt, 0); - if (r != PAM_SUCCESS) { - pam_syslog(handle, LOG_ERR, "Failed to set runtime dir."); - return r; - } - - r = export_legacy_dbus_address(handle, pw->pw_uid, rt); - if (r != PAM_SUCCESS) - return r; + r = pam_misc_setenv(handle, "XDG_RUNTIME_DIR", rt, 0); + if (r != PAM_SUCCESS) { + pam_syslog(handle, LOG_ERR, "Failed to set runtime dir."); + return r; } + r = export_legacy_dbus_address(handle, pw->pw_uid, rt); + if (r != PAM_SUCCESS) + return r; + return PAM_SUCCESS; } @@ -501,7 +493,7 @@ _public_ PAM_EXTERN int pam_sm_open_session( return PAM_SESSION_ERR; } - r = pam_set_data(handle, "systemd.session-fd", INT_TO_PTR(session_fd+1), NULL); + r = pam_set_data(handle, "systemd.session-fd", FD_TO_PTR(session_fd), NULL); if (r != PAM_SUCCESS) { pam_syslog(handle, LOG_ERR, "Failed to install session fd."); safe_close(session_fd); diff --git a/src/login/test-login-shared.c b/src/login/test-login-shared.c index 4c4275d124..ac327f71fb 100644 --- a/src/login/test-login-shared.c +++ b/src/login/test-login-shared.c @@ -19,8 +19,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include "macro.h" #include "login-util.h" +#include "macro.h" static void test_session_id_valid(void) { assert_se(session_id_valid("c1")); diff --git a/src/login/test-login-tables.c b/src/login/test-login-tables.c index a4196bf14b..4fbc893a9a 100644 --- a/src/login/test-login-tables.c +++ b/src/login/test-login-tables.c @@ -19,7 +19,6 @@ #include "logind-action.h" #include "logind-session.h" - #include "test-tables.h" int main(int argc, char **argv) { diff --git a/src/machine-id-setup/machine-id-setup-main.c b/src/machine-id-setup/machine-id-setup-main.c index f1165ea09c..d805bcfdca 100644 --- a/src/machine-id-setup/machine-id-setup-main.c +++ b/src/machine-id-setup/machine-id-setup-main.c @@ -26,8 +26,8 @@ #include "log.h" #include "machine-id-setup.h" -#include "util.h" #include "path-util.h" +#include "util.h" static char *arg_root = NULL; static bool arg_commit = false; diff --git a/src/machine/machine.c b/src/machine/machine.c index 196bc4b8f4..6b1fae2769 100644 --- a/src/machine/machine.c +++ b/src/machine/machine.c @@ -29,6 +29,7 @@ #include "bus-error.h" #include "bus-util.h" #include "escape.h" +#include "extract-word.h" #include "fd-util.h" #include "fileio.h" #include "formats-util.h" @@ -37,12 +38,12 @@ #include "machine.h" #include "mkdir.h" #include "parse-util.h" +#include "process-util.h" #include "special.h" #include "string-table.h" #include "terminal-util.h" #include "unit-name.h" #include "util.h" -#include "extract-word.h" Machine* machine_new(Manager *manager, MachineClass class, const char *name) { Machine *m; @@ -105,7 +106,7 @@ void machine_free(Machine *m) { m->manager->host_machine = NULL; if (m->leader > 0) - (void) hashmap_remove_value(m->manager->machine_leaders, UINT_TO_PTR(m->leader), m); + (void) hashmap_remove_value(m->manager->machine_leaders, PID_TO_PTR(m->leader), m); sd_bus_message_unref(m->create_message); @@ -401,7 +402,7 @@ int machine_start(Machine *m, sd_bus_message *properties, sd_bus_error *error) { if (m->started) return 0; - r = hashmap_put(m->manager->machine_leaders, UINT_TO_PTR(m->leader), m); + r = hashmap_put(m->manager->machine_leaders, PID_TO_PTR(m->leader), m); if (r < 0) return r; diff --git a/src/machine/machined-dbus.c b/src/machine/machined-dbus.c index 7827f063c1..961767c4a9 100644 --- a/src/machine/machined-dbus.c +++ b/src/machine/machined-dbus.c @@ -199,6 +199,9 @@ static int method_get_machine_by_pid(sd_bus_message *message, void *userdata, sd if (r < 0) return r; + if (pid < 0) + return -EINVAL; + if (pid == 0) { _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL; @@ -1505,7 +1508,7 @@ int manager_get_machine_by_pid(Manager *m, pid_t pid, Machine **machine) { assert(pid >= 1); assert(machine); - mm = hashmap_get(m->machine_leaders, UINT_TO_PTR(pid)); + mm = hashmap_get(m->machine_leaders, PID_TO_PTR(pid)); if (!mm) { _cleanup_free_ char *unit = NULL; diff --git a/src/machine/machined.h b/src/machine/machined.h index dac7a29ed1..bc5d4abb80 100644 --- a/src/machine/machined.h +++ b/src/machine/machined.h @@ -31,9 +31,9 @@ typedef struct Manager Manager; -#include "machine.h" -#include "machine-dbus.h" #include "image-dbus.h" +#include "machine-dbus.h" +#include "machine.h" struct Manager { sd_event *event; diff --git a/src/machine/test-machine-tables.c b/src/machine/test-machine-tables.c index 4aae426050..f851a4d37d 100644 --- a/src/machine/test-machine-tables.c +++ b/src/machine/test-machine-tables.c @@ -18,7 +18,6 @@ ***/ #include "machine.h" - #include "test-tables.h" int main(int argc, char **argv) { diff --git a/src/network/networkctl.c b/src/network/networkctl.c index ba7e3ba74a..6fcb3050c7 100644 --- a/src/network/networkctl.c +++ b/src/network/networkctl.c @@ -36,7 +36,6 @@ #include "lldp.h" #include "local-addresses.h" #include "locale-util.h" -#include "locale-util.h" #include "netlink-util.h" #include "pager.h" #include "parse-util.h" @@ -910,12 +909,10 @@ static int link_lldp_status(int argc, char *argv[], void *userdata) { _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL, *reply = NULL; _cleanup_netlink_unref_ sd_netlink *rtnl = NULL; _cleanup_free_ LinkInfo *links = NULL; - const char *state, *word; - double ttl = -1; uint32_t capability; int i, r, c, j; - size_t ll; + const char *p; char **s; pager_open_if_enabled(); @@ -956,14 +953,19 @@ static int link_lldp_status(int argc, char *argv[], void *userdata) { return -ENOMEM; STRV_FOREACH(s, l) { - FOREACH_WORD_QUOTED(word, ll, *s, state) { - _cleanup_free_ char *t = NULL, *a = NULL, *b = NULL; - t = strndup(word, ll); - if (!t) - return -ENOMEM; + p = *s; + for (;;) { + _cleanup_free_ char *a = NULL, *b = NULL, *word = NULL; + + r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES); + if (r < 0) + return log_error_errno(r, "Failed to parse LLDP syntax \"%s\": %m", *s); + + if (r == 0) + break; - r = split_pair(t, "=", &a, &b); + r = split_pair(word, "=", &a, &b); if (r < 0) continue; diff --git a/src/network/networkd-address-pool.h b/src/network/networkd-address-pool.h index e6207ccce6..7f5bdf1d2f 100644 --- a/src/network/networkd-address-pool.h +++ b/src/network/networkd-address-pool.h @@ -23,6 +23,7 @@ typedef struct AddressPool AddressPool; +#include "in-addr-util.h" #include "networkd.h" struct AddressPool { diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c index 8b6acf2e1d..1ce1f4d8d6 100644 --- a/src/network/networkd-address.c +++ b/src/network/networkd-address.c @@ -248,6 +248,8 @@ static int address_add_internal(Link *link, Set **addresses, address->family = family; address->in_addr = *in_addr; address->prefixlen = prefixlen; + /* Consider address tentative until we get the real flags from the kernel */ + address->flags = IFA_F_TENTATIVE; r = set_ensure_allocated(addresses, &address_hash_ops); if (r < 0) @@ -327,13 +329,13 @@ static int address_release(Address *address) { int address_update(Address *address, unsigned char flags, unsigned char scope, struct ifa_cacheinfo *cinfo) { bool ready; + int r; assert(address); assert(cinfo); ready = address_is_ready(address); - address->added = true; address->flags = flags; address->scope = scope; address->cinfo = *cinfo; @@ -341,8 +343,17 @@ int address_update(Address *address, unsigned char flags, unsigned char scope, s if (address->link) { link_update_operstate(address->link); - if (!ready && address_is_ready(address)) + if (!ready && address_is_ready(address)) { link_check_ready(address->link); + + if (address->family == AF_INET6 && + in_addr_is_link_local(AF_INET6, &address->in_addr) > 0 && + in_addr_is_null(AF_INET6, (const union in_addr_union*) &address->link->ipv6ll_address) > 0) { + r = link_ipv6ll_gained(address->link, &address->in_addr.in6); + if (r < 0) + return r; + } + } } return 0; @@ -769,5 +780,5 @@ int config_parse_label(const char *unit, bool address_is_ready(const Address *a) { assert(a); - return a->added && !(a->flags & (IFA_F_TENTATIVE | IFA_F_DEPRECATED)); + return !(a->flags & (IFA_F_TENTATIVE | IFA_F_DEPRECATED)); } diff --git a/src/network/networkd-address.h b/src/network/networkd-address.h index 0b1f3b688b..accd0a027d 100644 --- a/src/network/networkd-address.h +++ b/src/network/networkd-address.h @@ -28,9 +28,9 @@ typedef struct Address Address; -#include "networkd.h" -#include "networkd-network.h" #include "networkd-link.h" +#include "networkd-network.h" +#include "networkd.h" #define CACHE_INFO_INFINITY_LIFE_TIME 0xFFFFFFFFU @@ -52,7 +52,6 @@ struct Address { union in_addr_union in_addr; union in_addr_union in_addr_peer; - bool added:1; bool ip_masquerade_done:1; LIST_FIELDS(Address, addresses); diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c index b58fc5808c..48e3d84055 100644 --- a/src/network/networkd-dhcp4.c +++ b/src/network/networkd-dhcp4.c @@ -34,7 +34,7 @@ static int dhcp4_route_handler(sd_netlink *rtnl, sd_netlink_message *m, int r; assert(link); - assert(link->dhcp4_messages); + assert(link->dhcp4_messages > 0); link->dhcp4_messages --; @@ -44,7 +44,7 @@ static int dhcp4_route_handler(sd_netlink *rtnl, sd_netlink_message *m, link_enter_failed(link); } - if (!link->dhcp4_messages) { + if (link->dhcp4_messages == 0) { link->dhcp4_configured = true; link_check_ready(link); } @@ -255,6 +255,7 @@ static int dhcp_lease_lost(Link *link) { } link->dhcp_lease = sd_dhcp_lease_unref(link->dhcp_lease); + link_dirty(link); link->dhcp4_configured = false; return 0; @@ -331,6 +332,7 @@ static int dhcp_lease_renew(sd_dhcp_client *client, Link *link) { sd_dhcp_lease_unref(link->dhcp_lease); link->dhcp4_configured = false; link->dhcp_lease = sd_dhcp_lease_ref(lease); + link_dirty(link); r = sd_dhcp_lease_get_address(lease, &address); if (r < 0) @@ -408,6 +410,7 @@ static int dhcp_lease_acquired(sd_dhcp_client *client, Link *link) { NULL); link->dhcp_lease = sd_dhcp_lease_ref(lease); + link_dirty(link); if (link->network->dhcp_mtu) { uint16_t mtu; diff --git a/src/network/networkd-dhcp6.c b/src/network/networkd-dhcp6.c index c3332bb1ac..e67e51f7ef 100644 --- a/src/network/networkd-dhcp6.c +++ b/src/network/networkd-dhcp6.c @@ -131,7 +131,8 @@ static void dhcp6_handler(sd_dhcp6_client *client, int event, void *userdata) { case SD_DHCP6_CLIENT_EVENT_STOP: case SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE: case SD_DHCP6_CLIENT_EVENT_RETRANS_MAX: - log_link_warning(link, "DHCPv6 lease lost"); + if (sd_dhcp6_client_get_lease(client, NULL) >= 0) + log_link_warning(link, "DHCPv6 lease lost"); link->dhcp6_configured = false; break; @@ -165,83 +166,85 @@ static void dhcp6_handler(sd_dhcp6_client *client, int event, void *userdata) { link_check_ready(link); } -int dhcp6_configure(Link *link, bool inf_req) { - int r, information_request; +int dhcp6_request_address(Link *link) { + int r, inf_req; + bool running; - assert_return(link, -EINVAL); + assert(link); + assert(link->dhcp6_client); - link->dhcp6_configured = false; + r = sd_dhcp6_client_get_information_request(link->dhcp6_client, &inf_req); + if (r < 0) + return r; - if (link->dhcp6_client) { - r = sd_dhcp6_client_get_information_request(link->dhcp6_client, &information_request); - if (r < 0) { - log_link_warning_errno(link, r, "Could not get DHCPv6 Information request setting: %m"); - goto error; - } + if (!inf_req) + return 0; - if (information_request && !inf_req) { - r = sd_dhcp6_client_stop(link->dhcp6_client); - if (r < 0) { - log_link_warning_errno(link, r, "Could not stop DHCPv6 while setting Managed mode: %m"); - goto error; - } + r = sd_dhcp6_client_is_running(link->dhcp6_client); + if (r < 0) + return r; + else + running = !!r; - r = sd_dhcp6_client_set_information_request(link->dhcp6_client, false); - if (r < 0) { - log_link_warning_errno(link, r, "Could not unset DHCPv6 Information request: %m"); - goto error; - } + if (running) { + r = sd_dhcp6_client_stop(link->dhcp6_client); + if (r < 0) + return r; + } - } + r = sd_dhcp6_client_set_information_request(link->dhcp6_client, false); + if (r < 0) + return r; + if (running) { r = sd_dhcp6_client_start(link->dhcp6_client); - if (r < 0 && r != -EALREADY) { - log_link_warning_errno(link, r, "Could not restart DHCPv6: %m"); - goto error; - } + if (r < 0) + return r; + } - if (r == -EALREADY) - link->dhcp6_configured = true; + return 0; +} + +int dhcp6_configure(Link *link) { + sd_dhcp6_client *client = NULL; + int r; + assert(link); + + if (link->dhcp6_client) + return 0; + + r = sd_dhcp6_client_new(&client); + if (r < 0) return r; - } - r = sd_dhcp6_client_new(&link->dhcp6_client); + r = sd_dhcp6_client_attach_event(client, NULL, 0); if (r < 0) goto error; - r = sd_dhcp6_client_attach_event(link->dhcp6_client, NULL, 0); + r = sd_dhcp6_client_set_information_request(client, true); if (r < 0) goto error; - r = sd_dhcp6_client_set_mac(link->dhcp6_client, + r = sd_dhcp6_client_set_mac(client, (const uint8_t *) &link->mac, sizeof (link->mac), ARPHRD_ETHER); if (r < 0) goto error; - r = sd_dhcp6_client_set_index(link->dhcp6_client, link->ifindex); + r = sd_dhcp6_client_set_index(client, link->ifindex); if (r < 0) goto error; - r = sd_dhcp6_client_set_callback(link->dhcp6_client, dhcp6_handler, - link); + r = sd_dhcp6_client_set_callback(client, dhcp6_handler, link); if (r < 0) goto error; - if (inf_req) { - r = sd_dhcp6_client_set_information_request(link->dhcp6_client, true); - if (r < 0) - goto error; - } - - r = sd_dhcp6_client_start(link->dhcp6_client); - if (r < 0) - goto error; + link->dhcp6_client = client; - return r; + return 0; - error: - link->dhcp6_client = sd_dhcp6_client_unref(link->dhcp6_client); +error: + sd_dhcp6_client_unref(client); return r; } diff --git a/src/network/networkd-fdb.c b/src/network/networkd-fdb.c index c9222b8cb8..6e5480ee22 100644 --- a/src/network/networkd-fdb.c +++ b/src/network/networkd-fdb.c @@ -19,8 +19,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <net/if.h> #include <net/ethernet.h> +#include <net/if.h> #include "alloc-util.h" #include "conf-parser.h" diff --git a/src/network/networkd-fdb.h b/src/network/networkd-fdb.h index f0efb902d0..c8e3f2ce56 100644 --- a/src/network/networkd-fdb.h +++ b/src/network/networkd-fdb.h @@ -23,8 +23,8 @@ typedef struct FdbEntry FdbEntry; -#include "networkd.h" #include "networkd-network.h" +#include "networkd.h" struct FdbEntry { Network *network; diff --git a/src/network/networkd-ipv4ll.c b/src/network/networkd-ipv4ll.c index ed0d861e7a..f4aac4bb93 100644 --- a/src/network/networkd-ipv4ll.c +++ b/src/network/networkd-ipv4ll.c @@ -22,8 +22,8 @@ #include <netinet/ether.h> #include <linux/if.h> -#include "networkd-link.h" #include "network-internal.h" +#include "networkd-link.h" static int ipv4ll_address_lost(Link *link) { _cleanup_address_free_ Address *address = NULL; @@ -201,7 +201,7 @@ static void ipv4ll_handler(sd_ipv4ll *ll, int event, void *userdata){ } int ipv4ll_configure(Link *link) { - uint8_t seed[8]; + uint64_t seed; int r; assert(link); @@ -215,11 +215,11 @@ int ipv4ll_configure(Link *link) { } if (link->udev_device) { - r = net_get_unique_predictable_data(link->udev_device, seed); + r = net_get_unique_predictable_data(link->udev_device, &seed); if (r >= 0) { assert_cc(sizeof(unsigned) <= 8); - r = sd_ipv4ll_set_address_seed(link->ipv4ll, *(unsigned *)seed); + r = sd_ipv4ll_set_address_seed(link->ipv4ll, (unsigned)seed); if (r < 0) return r; } diff --git a/src/network/networkd-link-bus.c b/src/network/networkd-link-bus.c index 11b35d6cf8..d09a3c2d07 100644 --- a/src/network/networkd-link-bus.c +++ b/src/network/networkd-link-bus.c @@ -19,13 +19,12 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include "bus-util.h" -#include "strv.h" - #include "alloc-util.h" +#include "bus-util.h" #include "networkd-link.h" #include "networkd.h" #include "parse-util.h" +#include "strv.h" static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_operational_state, link_operstate, LinkOperationalState); static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_administrative_state, link_state, LinkState); diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 13d2fc6d0d..a9d91b07f6 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -111,20 +111,56 @@ static bool link_ipv4_forward_enabled(Link *link) { if (!link->network) return false; + if (link->network->ip_forward == _ADDRESS_FAMILY_BOOLEAN_INVALID) + return false; + return link->network->ip_forward & ADDRESS_FAMILY_IPV4; } static bool link_ipv6_forward_enabled(Link *link) { + + if (!socket_ipv6_is_supported()) + return false; + if (link->flags & IFF_LOOPBACK) return false; if (!link->network) return false; + if (link->network->ip_forward == _ADDRESS_FAMILY_BOOLEAN_INVALID) + return false; + return link->network->ip_forward & ADDRESS_FAMILY_IPV6; } +bool link_ipv6_accept_ra_enabled(Link *link) { + if (link->flags & IFF_LOOPBACK) + return false; + + if (!link->network) + return false; + + /* If unset use system default (enabled if local forwarding is disabled. + * disabled if local forwarding is enabled). + * If set, ignore or enforce RA independent of local forwarding state. + */ + if (link->network->ipv6_accept_ra < 0) + /* default to accept RA if ip_forward is disabled and ignore RA if ip_forward is enabled */ + return !link_ipv6_forward_enabled(link); + else if (link->network->ipv6_accept_ra > 0) + /* accept RA even if ip_forward is enabled */ + return true; + else + /* ignore RA */ + return false; +} + static IPv6PrivacyExtensions link_ipv6_privacy_extensions(Link *link) { + + if (!socket_ipv6_is_supported()) + return _IPV6_PRIVACY_EXTENSIONS_INVALID; + if (link->flags & IFF_LOOPBACK) return _IPV6_PRIVACY_EXTENSIONS_INVALID; @@ -470,9 +506,6 @@ static int link_stop_clients(Link *link) { assert(link->manager); assert(link->manager->event); - if (!link->network) - return 0; - if (link->dhcp_client) { k = sd_dhcp_client_stop(link->dhcp_client); if (k < 0) @@ -485,13 +518,13 @@ static int link_stop_clients(Link *link) { r = log_link_warning_errno(link, r, "Could not stop IPv4 link-local: %m"); } - if(link->ndisc_router_discovery) { - if (link->dhcp6_client) { - k = sd_dhcp6_client_stop(link->dhcp6_client); - if (k < 0) - r = log_link_warning_errno(link, r, "Could not stop DHCPv6 client: %m"); - } + if (link->dhcp6_client) { + k = sd_dhcp6_client_stop(link->dhcp6_client); + if (k < 0) + r = log_link_warning_errno(link, r, "Could not stop DHCPv6 client: %m"); + } + if (link->ndisc_router_discovery) { k = sd_ndisc_stop(link->ndisc_router_discovery); if (k < 0) r = log_link_warning_errno(link, r, "Could not stop IPv6 Router Discovery: %m"); @@ -581,6 +614,10 @@ void link_check_ready(Link *link) { !link->ipv4ll_route) return; + if (link_ipv6ll_enabled(link)) + if (in_addr_is_null(AF_INET6, (const union in_addr_union*) &link->ipv6ll_address) > 0) + return; + if ((link_dhcp4_enabled(link) && !link_dhcp6_enabled(link) && !link->dhcp4_configured) || (link_dhcp6_enabled(link) && !link_dhcp4_enabled(link) && @@ -589,6 +626,9 @@ void link_check_ready(Link *link) { !link->dhcp4_configured && !link->dhcp6_configured)) return; + if (link_ipv6_accept_ra_enabled(link) && !link->ndisc_configured) + return; + SET_FOREACH(a, link->addresses, i) if (!address_is_ready(a)) return; @@ -1213,6 +1253,39 @@ static void lldp_handler(sd_lldp *lldp, int event, void *userdata) { } } +static int link_acquire_ipv6_conf(Link *link) { + int r; + + assert(link); + + if (link_dhcp6_enabled(link)) { + assert(link->dhcp6_client); + assert(in_addr_is_link_local(AF_INET6, (const union in_addr_union*)&link->ipv6ll_address) > 0); + + log_link_debug(link, "Acquiring DHCPv6 lease"); + + r = sd_dhcp6_client_set_local_address(link->dhcp6_client, &link->ipv6ll_address); + if (r < 0 && r != -EBUSY) + return log_link_warning_errno(link, r, "Could not set IPv6LL address in DHCP client: %m"); + + r = sd_dhcp6_client_start(link->dhcp6_client); + if (r < 0 && r != -EBUSY) + return log_link_warning_errno(link, r, "Could not acquire DHCPv6 lease: %m"); + } + + if (link_ipv6_accept_ra_enabled(link)) { + assert(link->ndisc_router_discovery); + + log_link_debug(link, "Discovering IPv6 routers"); + + r = sd_ndisc_router_discovery_start(link->ndisc_router_discovery); + if (r < 0 && r != -EBUSY) + return log_link_warning_errno(link, r, "Could not start IPv6 Router Discovery: %m"); + } + + return 0; +} + static int link_acquire_conf(Link *link) { int r; @@ -1241,16 +1314,6 @@ static int link_acquire_conf(Link *link) { return log_link_warning_errno(link, r, "Could not acquire DHCPv4 lease: %m"); } - if (link_dhcp6_enabled(link)) { - assert(link->ndisc_router_discovery); - - log_link_debug(link, "Discovering IPv6 routers"); - - r = sd_ndisc_router_discovery_start(link->ndisc_router_discovery); - if (r < 0) - return log_link_warning_errno(link, r, "Could not start IPv6 Router Discovery: %m"); - } - if (link_lldp_enabled(link)) { assert(link->lldp); @@ -1800,55 +1863,43 @@ static int link_enter_join_netdev(Link *link) { } static int link_set_ipv4_forward(Link *link) { - const char *p = NULL, *v; int r; - if (link->flags & IFF_LOOPBACK) - return 0; - - if (link->network->ip_forward == _ADDRESS_FAMILY_BOOLEAN_INVALID) + if (!link_ipv4_forward_enabled(link)) return 0; - p = strjoina("/proc/sys/net/ipv4/conf/", link->ifname, "/forwarding"); - v = one_zero(link_ipv4_forward_enabled(link)); + /* We propagate the forwarding flag from one interface to the + * global setting one way. This means: as long as at least one + * interface was configured at any time that had IP forwarding + * enabled the setting will stay on for good. We do this + * primarily to keep IPv4 and IPv6 packet forwarding behaviour + * somewhat in sync (see below). */ - r = write_string_file(p, v, 0); - if (r < 0) { - /* If the right value is set anyway, don't complain */ - if (verify_one_line_file(p, v) > 0) - return 0; - - log_link_warning_errno(link, r, "Cannot configure IPv4 forwarding for interface %s: %m", link->ifname); - } + r = write_string_file("/proc/sys/net/ipv4/ip_forward", "1", WRITE_STRING_FILE_VERIFY_ON_FAILURE); + if (r < 0) + log_link_warning_errno(link, r, "Cannot turn on IPv4 packet forwarding, ignoring: %m"); return 0; } static int link_set_ipv6_forward(Link *link) { - const char *p = NULL, *v = NULL; int r; - /* Make this a NOP if IPv6 is not available */ - if (!socket_ipv6_is_supported()) - return 0; - - if (link->flags & IFF_LOOPBACK) + if (!link_ipv6_forward_enabled(link)) return 0; - if (link->network->ip_forward == _ADDRESS_FAMILY_BOOLEAN_INVALID) - return 0; + /* On Linux, the IPv6 stack does not not know a per-interface + * packet forwarding setting: either packet forwarding is on + * for all, or off for all. We hence don't bother with a + * per-interface setting, but simply propagate the interface + * flag, if it is set, to the global flag, one-way. Note that + * while IPv4 would allow a per-interface flag, we expose the + * same behaviour there and also propagate the setting from + * one to all, to keep things simple (see above). */ - p = strjoina("/proc/sys/net/ipv6/conf/", link->ifname, "/forwarding"); - v = one_zero(link_ipv6_forward_enabled(link)); - - r = write_string_file(p, v, 0); - if (r < 0) { - /* If the right value is set anyway, don't complain */ - if (verify_one_line_file(p, v) > 0) - return 0; - - log_link_warning_errno(link, r, "Cannot configure IPv6 forwarding for interface: %m"); - } + r = write_string_file("/proc/sys/net/ipv6/conf/all/forwarding", "1", WRITE_STRING_FILE_VERIFY_ON_FAILURE); + if (r < 0) + log_link_warning_errno(link, r, "Cannot configure IPv6 packet forwarding, ignoring: %m"); return 0; } @@ -1859,31 +1910,22 @@ static int link_set_ipv6_privacy_extensions(Link *link) { const char *p = NULL; int r; - /* Make this a NOP if IPv6 is not available */ - if (!socket_ipv6_is_supported()) - return 0; - s = link_ipv6_privacy_extensions(link); - if (s == _IPV6_PRIVACY_EXTENSIONS_INVALID) + if (s < 0) return 0; p = strjoina("/proc/sys/net/ipv6/conf/", link->ifname, "/use_tempaddr"); - xsprintf(buf, "%u", link->network->ipv6_privacy_extensions); - - r = write_string_file(p, buf, 0); - if (r < 0) { - /* If the right value is set anyway, don't complain */ - if (verify_one_line_file(p, buf) > 0) - return 0; + xsprintf(buf, "%u", (unsigned) link->network->ipv6_privacy_extensions); + r = write_string_file(p, buf, WRITE_STRING_FILE_VERIFY_ON_FAILURE); + if (r < 0) log_link_warning_errno(link, r, "Cannot configure IPv6 privacy extension for interface: %m"); - } return 0; } static int link_set_ipv6_accept_ra(Link *link) { - const char *p = NULL, *v = NULL; + const char *p = NULL; int r; /* Make this a NOP if IPv6 is not available */ @@ -1893,36 +1935,21 @@ static int link_set_ipv6_accept_ra(Link *link) { if (link->flags & IFF_LOOPBACK) return 0; - /* If unset use system default (enabled if local forwarding is disabled. - * disabled if local forwarding is enabled). - * If set, ignore or enforce RA independent of local forwarding state. - */ - if (link->network->ipv6_accept_ra < 0) - /* default to accept RA if ip_forward is disabled and ignore RA if ip_forward is enabled */ - v = "1"; - else if (link->network->ipv6_accept_ra > 0) - /* "2" means accept RA even if ip_forward is enabled */ - v = "2"; - else - /* "0" means ignore RA */ - v = "0"; + if (!link->network) + return 0; p = strjoina("/proc/sys/net/ipv6/conf/", link->ifname, "/accept_ra"); - r = write_string_file(p, v, 0); - if (r < 0) { - /* If the right value is set anyway, don't complain */ - if (verify_one_line_file(p, v) > 0) - return 0; - - log_link_warning_errno(link, r, "Cannot configure IPv6 accept_ra for interface: %m"); - } + /* We handle router advertisments ourselves, tell the kernel to GTFO */ + r = write_string_file(p, "0", WRITE_STRING_FILE_VERIFY_ON_FAILURE); + if (r < 0) + log_link_warning_errno(link, r, "Cannot disable kernel IPv6 accept_ra for interface: %m"); return 0; } static int link_set_ipv6_dad_transmits(Link *link) { - char buf[DECIMAL_STR_MAX(unsigned) + 1]; + char buf[DECIMAL_STR_MAX(int) + 1]; const char *p = NULL; int r; @@ -1933,27 +1960,24 @@ static int link_set_ipv6_dad_transmits(Link *link) { if (link->flags & IFF_LOOPBACK) return 0; + if (!link->network) + return 0; + if (link->network->ipv6_dad_transmits < 0) return 0; p = strjoina("/proc/sys/net/ipv6/conf/", link->ifname, "/dad_transmits"); + xsprintf(buf, "%i", link->network->ipv6_dad_transmits); - xsprintf(buf, "%u", link->network->ipv6_dad_transmits); - - r = write_string_file(p, buf, 0); - if (r < 0) { - /* If the right value is set anyway, don't complain */ - if (verify_one_line_file(p, buf) > 0) - return 0; - + r = write_string_file(p, buf, WRITE_STRING_FILE_VERIFY_ON_FAILURE); + if (r < 0) log_link_warning_errno(link, r, "Cannot set IPv6 dad transmits for interface: %m"); - } return 0; } static int link_set_ipv6_hop_limit(Link *link) { - char buf[DECIMAL_STR_MAX(unsigned) + 1]; + char buf[DECIMAL_STR_MAX(int) + 1]; const char *p = NULL; int r; @@ -1964,20 +1988,46 @@ static int link_set_ipv6_hop_limit(Link *link) { if (link->flags & IFF_LOOPBACK) return 0; + if (!link->network) + return 0; + if (link->network->ipv6_hop_limit < 0) return 0; p = strjoina("/proc/sys/net/ipv6/conf/", link->ifname, "/hop_limit"); + xsprintf(buf, "%i", link->network->ipv6_hop_limit); - xsprintf(buf, "%u", link->network->ipv6_hop_limit); + r = write_string_file(p, buf, WRITE_STRING_FILE_VERIFY_ON_FAILURE); + if (r < 0) + log_link_warning_errno(link, r, "Cannot set IPv6 hop limit for interface: %m"); - r = write_string_file(p, buf, 0); - if (r < 0) { - /* If the right value is set anyway, don't complain */ - if (verify_one_line_file(p, buf) > 0) - return 0; + return 0; +} - log_link_warning_errno(link, r, "Cannot set IPv6 hop limit for interface: %m"); +static int link_drop_foreign_config(Link *link) { + Address *address; + Route *route; + Iterator i; + int r; + + SET_FOREACH(address, link->addresses_foreign, i) { + /* we consider IPv6LL addresses to be managed by the kernel */ + if (address->family == AF_INET6 && in_addr_is_link_local(AF_INET6, &address->in_addr) == 1) + continue; + + r = address_remove(address, link, link_address_remove_handler); + if (r < 0) + return r; + } + + SET_FOREACH(route, link->routes_foreign, i) { + /* do not touch routes managed by the kernel */ + if (route->protocol == RTPROT_KERNEL) + continue; + + r = route_remove(route, link, link_address_remove_handler); + if (r < 0) + return r; } return 0; @@ -1990,6 +2040,14 @@ static int link_configure(Link *link) { assert(link->network); assert(link->state == LINK_STATE_PENDING); + /* Drop foreign config, but ignore loopback device. + * We do not want to remove loopback address. */ + if (!(link->flags & IFF_LOOPBACK)) { + r = link_drop_foreign_config(link); + if (r < 0) + return r; + } + r = link_set_bridge_fdb(link); if (r < 0) return r; @@ -2040,7 +2098,14 @@ static int link_configure(Link *link) { return r; } - if (link_dhcp6_enabled(link)) { + if (link_dhcp6_enabled(link) || + link_ipv6_accept_ra_enabled(link)) { + r = dhcp6_configure(link); + if (r < 0) + return r; + } + + if (link_ipv6_accept_ra_enabled(link)) { r = ndisc_configure(link); if (r < 0) return r; @@ -2065,6 +2130,12 @@ static int link_configure(Link *link) { r = link_acquire_conf(link); if (r < 0) return r; + + if (in_addr_is_null(AF_INET6, (const union in_addr_union*) &link->ipv6ll_address) == 0) { + r = link_acquire_ipv6_conf(link); + if (r < 0) + return r; + } } return link_enter_join_netdev(link); @@ -2229,7 +2300,8 @@ network_file_fail: if (r < 0) { log_link_debug_errno(link, r, "Failed to extract next address string: %m"); continue; - } if (r == 0) + } + if (r == 0) break; prefixlen_str = strchr(address_str, '/'); @@ -2259,6 +2331,8 @@ network_file_fail: } if (routes) { + p = routes; + for (;;) { Route *route; _cleanup_free_ char *route_str = NULL; @@ -2273,7 +2347,8 @@ network_file_fail: if (r < 0) { log_link_debug_errno(link, r, "Failed to extract next route string: %m"); continue; - } if (r == 0) + } + if (r == 0) break; prefixlen_str = strchr(route_str, '/'); @@ -2411,12 +2486,33 @@ failed: return r; } +int link_ipv6ll_gained(Link *link, const struct in6_addr *address) { + int r; + + assert(link); + + log_link_info(link, "Gained IPv6LL"); + + link->ipv6ll_address = *address; + link_check_ready(link); + + if (!IN_SET(link->state, LINK_STATE_PENDING, LINK_STATE_PENDING, LINK_STATE_UNMANAGED, LINK_STATE_FAILED)) { + r = link_acquire_ipv6_conf(link); + if (r < 0) { + link_enter_failed(link); + return r; + } + } + + return 0; +} + static int link_carrier_gained(Link *link) { int r; assert(link); - if (link->network) { + if (!IN_SET(link->state, LINK_STATE_PENDING, LINK_STATE_PENDING, LINK_STATE_UNMANAGED, LINK_STATE_FAILED)) { r = link_acquire_conf(link); if (r < 0) { link_enter_failed(link); @@ -2639,9 +2735,8 @@ int link_save(Link *link) { sd_dhcp6_lease *dhcp6_lease = NULL; if (link->dhcp6_client) { - r = sd_dhcp6_client_get_lease(link->dhcp6_client, - &dhcp6_lease); - if (r < 0) + r = sd_dhcp6_client_get_lease(link->dhcp6_client, &dhcp6_lease); + if (r < 0 && r != -ENOMSG) log_link_debug(link, "No DHCPv6 lease"); } diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h index a22041870e..3964a12f37 100644 --- a/src/network/networkd-link.h +++ b/src/network/networkd-link.h @@ -56,9 +56,9 @@ typedef enum LinkOperationalState { _LINK_OPERSTATE_INVALID = -1 } LinkOperationalState; -#include "networkd.h" -#include "networkd-network.h" #include "networkd-address.h" +#include "networkd-network.h" +#include "networkd.h" struct Link { Manager *manager; @@ -69,6 +69,7 @@ struct Link { char *ifname; char *state_file; struct ether_addr mac; + struct in6_addr ipv6ll_address; uint32_t mtu; struct udev_device *udev_device; @@ -95,10 +96,12 @@ struct Link { unsigned dhcp4_messages; bool dhcp4_configured; bool dhcp6_configured; + unsigned ndisc_messages; + bool ndisc_configured; sd_ipv4ll *ipv4ll; - bool ipv4ll_address; - bool ipv4ll_route; + bool ipv4ll_address:1; + bool ipv4ll_route:1; bool static_configured; @@ -141,13 +144,16 @@ int link_save(Link *link); int link_carrier_reset(Link *link); bool link_has_carrier(Link *link); +int link_ipv6ll_gained(Link *link, const struct in6_addr *address); + int link_set_mtu(Link *link, uint32_t mtu); int link_set_hostname(Link *link, const char *hostname); int link_set_timezone(Link *link, const char *timezone); int ipv4ll_configure(Link *link); int dhcp4_configure(Link *link); -int dhcp6_configure(Link *link, bool information_request); +int dhcp6_configure(Link *link); +int dhcp6_request_address(Link *link); int ndisc_configure(Link *link); bool link_lldp_enabled(Link *link); @@ -156,6 +162,7 @@ bool link_ipv6ll_enabled(Link *link); bool link_dhcp4_server_enabled(Link *link); bool link_dhcp4_enabled(Link *link); bool link_dhcp6_enabled(Link *link); +bool link_ipv6_accept_ra_enabled(Link *link); const char* link_state_to_string(LinkState s) _const_; LinkState link_state_from_string(const char *s) _pure_; diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c index a5701001c1..42f58fed19 100644 --- a/src/network/networkd-manager.c +++ b/src/network/networkd-manager.c @@ -577,9 +577,7 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message, r = sd_netlink_message_read_cache_info(message, IFA_CACHEINFO, &cinfo); if (r >= 0) { - if (cinfo.ifa_valid == CACHE_INFO_INFINITY_LIFE_TIME) - valid_str = "ever"; - else + if (cinfo.ifa_valid != CACHE_INFO_INFINITY_LIFE_TIME) valid_str = format_timespan(valid_buf, FORMAT_TIMESPAN_MAX, cinfo.ifa_valid * USEC_PER_SEC, USEC_PER_SEC); @@ -590,7 +588,8 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message, switch (type) { case RTM_NEWADDR: if (address) - log_link_debug(link, "Updating address: %s/%u (valid for %s)", buf, prefixlen, valid_str); + log_link_debug(link, "Updating address: %s/%u (valid %s%s)", buf, prefixlen, + valid_str ? "for " : "forever", valid_str ?: ""); else { /* An address appeared that we did not request */ r = address_add_foreign(link, family, &in_addr, prefixlen, &address); @@ -598,7 +597,8 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message, log_link_warning_errno(link, r, "Failed to add address %s/%u: %m", buf, prefixlen); return 0; } else - log_link_debug(link, "Adding address: %s/%u (valid for %s)", buf, prefixlen, valid_str); + log_link_debug(link, "Adding address: %s/%u (valid %s%s)", buf, prefixlen, + valid_str ? "for " : "forever", valid_str ?: ""); } address_update(address, flags, scope, &cinfo); @@ -608,10 +608,12 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message, case RTM_DELADDR: if (address) { - log_link_debug(link, "Removing address: %s/%u (valid for %s)", buf, prefixlen, valid_str); + log_link_debug(link, "Removing address: %s/%u (valid %s%s)", buf, prefixlen, + valid_str ? "for " : "forever", valid_str ?: ""); address_drop(address); } else - log_link_warning(link, "Removing non-existent address: %s/%u (valid for %s)", buf, prefixlen, valid_str); + log_link_warning(link, "Removing non-existent address: %s/%u (valid %s%s)", buf, prefixlen, + valid_str ? "for " : "forever", valid_str ?: ""); break; default: @@ -1091,7 +1093,8 @@ static bool manager_check_idle(void *userdata) { link_ipv4ll_enabled(link) || link_dhcp4_server_enabled(link) || link_dhcp4_enabled(link) || - link_dhcp6_enabled(link)) + link_dhcp6_enabled(link) || + link_ipv6_accept_ra_enabled(link)) return false; } diff --git a/src/network/networkd-ndisc.c b/src/network/networkd-ndisc.c index 33e692f97f..ce9e513ceb 100644 --- a/src/network/networkd-ndisc.c +++ b/src/network/networkd-ndisc.c @@ -20,14 +20,132 @@ ***/ #include <netinet/ether.h> +#include <netinet/icmp6.h> #include <linux/if.h> #include "sd-ndisc.h" #include "networkd-link.h" -static void ndisc_router_handler(sd_ndisc *nd, int event, void *userdata) { +static int ndisc_netlink_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { + _cleanup_link_unref_ Link *link = userdata; + int r; + + assert(link); + assert(link->ndisc_messages > 0); + + link->ndisc_messages --; + + r = sd_netlink_message_get_errno(m); + if (r < 0 && r != -EEXIST) { + log_link_error_errno(link, r, "Could not set NDisc route or address: %m"); + link_enter_failed(link); + } + + if (link->ndisc_messages == 0) { + link->ndisc_configured = true; + link_check_ready(link); + } + + return 1; +} + +static void ndisc_prefix_autonomous_handler(sd_ndisc *nd, const struct in6_addr *prefix, unsigned prefixlen, + unsigned lifetime_preferred, unsigned lifetime_valid, void *userdata) { + _cleanup_address_free_ Address *address = NULL; Link *link = userdata; + usec_t time_now; + int r; + + assert(nd); + assert(link); + assert(link->network); + + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + return; + + r = address_new(&address); + if (r < 0) { + log_link_error_errno(link, r, "Could not allocate address: %m"); + return; + } + + assert_se(sd_event_now(link->manager->event, clock_boottime_or_monotonic(), &time_now) >= 0); + + address->family = AF_INET6; + address->in_addr.in6 = *prefix; + if (in_addr_is_null(AF_INET6, (const union in_addr_union *) &link->network->ipv6_token) == 0) + memcpy(((char *)&address->in_addr.in6) + 8, ((char *)&link->network->ipv6_token) + 8, 8); + else { + /* see RFC4291 section 2.5.1 */ + address->in_addr.in6.__in6_u.__u6_addr8[8] = link->mac.ether_addr_octet[0]; + address->in_addr.in6.__in6_u.__u6_addr8[8] ^= 1 << 1; + address->in_addr.in6.__in6_u.__u6_addr8[9] = link->mac.ether_addr_octet[1]; + address->in_addr.in6.__in6_u.__u6_addr8[10] = link->mac.ether_addr_octet[2]; + address->in_addr.in6.__in6_u.__u6_addr8[11] = 0xff; + address->in_addr.in6.__in6_u.__u6_addr8[12] = 0xfe; + address->in_addr.in6.__in6_u.__u6_addr8[13] = link->mac.ether_addr_octet[3]; + address->in_addr.in6.__in6_u.__u6_addr8[14] = link->mac.ether_addr_octet[4]; + address->in_addr.in6.__in6_u.__u6_addr8[15] = link->mac.ether_addr_octet[5]; + } + address->prefixlen = prefixlen; + address->flags = IFA_F_NOPREFIXROUTE; + address->cinfo.ifa_prefered = lifetime_preferred; + address->cinfo.ifa_valid = lifetime_valid; + + r = address_configure(address, link, ndisc_netlink_handler, true); + if (r < 0) { + log_link_warning_errno(link, r, "Could not set SLAAC address: %m"); + link_enter_failed(link); + return; + } + + link->ndisc_messages ++; +} + +static void ndisc_prefix_onlink_handler(sd_ndisc *nd, const struct in6_addr *prefix, unsigned prefixlen, unsigned lifetime, void *userdata) { + _cleanup_route_free_ Route *route = NULL; + Link *link = userdata; + usec_t time_now; + int r; + + assert(nd); + assert(link); + + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + return; + + r = route_new(&route); + if (r < 0) { + log_link_error_errno(link, r, "Could not allocate route: %m"); + return; + } + + assert_se(sd_event_now(link->manager->event, clock_boottime_or_monotonic(), &time_now) >= 0); + + route->family = AF_INET6; + route->table = RT_TABLE_MAIN; + route->protocol = RTPROT_RA; + route->flags = RTM_F_PREFIX; + route->dst.in6 = *prefix; + route->dst_prefixlen = prefixlen; + route->lifetime = time_now + lifetime * USEC_PER_SEC; + + r = route_configure(route, link, ndisc_netlink_handler); + if (r < 0) { + log_link_warning_errno(link, r, "Could not set prefix route: %m"); + link_enter_failed(link); + return; + } + + link->ndisc_messages ++; +} + +static void ndisc_router_handler(sd_ndisc *nd, uint8_t flags, const struct in6_addr *gateway, unsigned lifetime, int pref, void *userdata) { + _cleanup_route_free_ Route *route = NULL; + Link *link = userdata; + usec_t time_now; + int r; assert(link); assert(link->network); @@ -36,27 +154,68 @@ static void ndisc_router_handler(sd_ndisc *nd, int event, void *userdata) { if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) return; - switch(event) { - case SD_NDISC_EVENT_ROUTER_ADVERTISMENT_NONE: + if (flags & (ND_RA_FLAG_MANAGED | ND_RA_FLAG_OTHER)) { + if (flags & ND_RA_FLAG_MANAGED) + dhcp6_request_address(link); + + r = sd_dhcp6_client_start(link->dhcp6_client); + if (r < 0 && r != -EBUSY) + log_link_warning_errno(link, r, "Starting DHCPv6 client on NDisc request failed: %m"); + } + + if (!gateway) return; - case SD_NDISC_EVENT_ROUTER_ADVERTISMENT_OTHER: - dhcp6_configure(link, true); + r = route_new(&route); + if (r < 0) { + log_link_error_errno(link, r, "Could not allocate route: %m"); + return; + } - break; - case SD_NDISC_EVENT_ROUTER_ADVERTISMENT_TIMEOUT: - case SD_NDISC_EVENT_ROUTER_ADVERTISMENT_MANAGED: - dhcp6_configure(link, false); + assert_se(sd_event_now(link->manager->event, clock_boottime_or_monotonic(), &time_now) >= 0); - break; + route->family = AF_INET6; + route->table = RT_TABLE_MAIN; + route->protocol = RTPROT_RA; + route->pref = pref; + route->gw.in6 = *gateway; + route->lifetime = time_now + lifetime * USEC_PER_SEC; - default: - if (event < 0) - log_link_warning_errno(link, event, "IPv6 Neighbor Discover error: %m"); - else - log_link_warning(link, "IPv6 Neighbor Discovery unknown event: %d", event); + r = route_configure(route, link, ndisc_netlink_handler); + if (r < 0) { + log_link_warning_errno(link, r, "Could not set default route: %m"); + link_enter_failed(link); + return; + } + + link->ndisc_messages ++; +} + +static void ndisc_handler(sd_ndisc *nd, int event, void *userdata) { + Link *link = userdata; + int r; + + assert(link); + + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + return; + + switch (event) { + case SD_NDISC_EVENT_TIMEOUT: + dhcp6_request_address(link); + + r = sd_dhcp6_client_start(link->dhcp6_client); + if (r < 0 && r != -EBUSY) + log_link_warning_errno(link, r, "Starting DHCPv6 client after NDisc timeout failed: %m"); + + link->ndisc_configured = true; + link_check_ready(link); break; + case SD_NDISC_EVENT_STOP: + break; + default: + log_link_warning(link, "IPv6 Neighbor Discovery unknown event: %d", event); } } @@ -82,7 +241,11 @@ int ndisc_configure(Link *link) { return r; r = sd_ndisc_set_callback(link->ndisc_router_discovery, - ndisc_router_handler, link); + ndisc_router_handler, + ndisc_prefix_onlink_handler, + ndisc_prefix_autonomous_handler, + ndisc_handler, + link); return r; } diff --git a/src/network/networkd-netdev-bond.c b/src/network/networkd-netdev-bond.c index 70105b8aa0..50b9021d09 100644 --- a/src/network/networkd-netdev-bond.c +++ b/src/network/networkd-netdev-bond.c @@ -29,8 +29,8 @@ #include "conf-parser.h" #include "missing.h" #include "networkd-netdev-bond.h" -#include "string-util.h" #include "string-table.h" +#include "string-util.h" /* * Number of seconds between instances where the bonding @@ -340,8 +340,6 @@ int config_parse_arp_ip_target_address(const char *unit, void *data, void *userdata) { Bond *b = userdata; - const char *word, *state; - size_t l; int r; assert(filename); @@ -349,14 +347,19 @@ int config_parse_arp_ip_target_address(const char *unit, assert(rvalue); assert(data); - FOREACH_WORD_QUOTED(word, l, rvalue, state) { + for (;;) { _cleanup_free_ ArpIpTarget *buffer = NULL; _cleanup_free_ char *n = NULL; int f; - n = strndup(word, l); - if (!n) - return -ENOMEM; + r = extract_first_word(&rvalue, &n, NULL, 0); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse Bond ARP ip target address, ignoring assignment: %s", rvalue); + return 0; + } + + if (r == 0) + break; buffer = new0(ArpIpTarget, 1); if (!buffer) @@ -380,7 +383,9 @@ int config_parse_arp_ip_target_address(const char *unit, } if (b->n_arp_ip_targets > NETDEV_BOND_ARP_TARGETS_MAX) - log_syntax(unit, LOG_WARNING, filename, line, 0, "More than the maximum number of kernel-supported ARP ip targets specified: %d > %d", b->n_arp_ip_targets, NETDEV_BOND_ARP_TARGETS_MAX); + log_syntax(unit, LOG_WARNING, filename, line, 0, + "More than the maximum number of kernel-supported ARP ip targets specified: %d > %d", + b->n_arp_ip_targets, NETDEV_BOND_ARP_TARGETS_MAX); return 0; } diff --git a/src/network/networkd-netdev-bridge.c b/src/network/networkd-netdev-bridge.c index 57c58d83b4..a991bdb5fb 100644 --- a/src/network/networkd-netdev-bridge.c +++ b/src/network/networkd-netdev-bridge.c @@ -22,9 +22,9 @@ #include <net/if.h> -#include "networkd-netdev-bridge.h" #include "missing.h" #include "netlink-util.h" +#include "networkd-netdev-bridge.h" /* callback for brige netdev's parameter set */ static int netdev_bridge_set_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { diff --git a/src/network/networkd-netdev-tuntap.c b/src/network/networkd-netdev-tuntap.c index 851e83537e..3d504a8564 100644 --- a/src/network/networkd-netdev-tuntap.c +++ b/src/network/networkd-netdev-tuntap.c @@ -19,8 +19,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <sys/ioctl.h> #include <net/if.h> +#include <sys/ioctl.h> #include <linux/if_tun.h> #include "alloc-util.h" diff --git a/src/network/networkd-netdev-veth.c b/src/network/networkd-netdev-veth.c index bee1a16726..773a1ee6d1 100644 --- a/src/network/networkd-netdev-veth.c +++ b/src/network/networkd-netdev-veth.c @@ -23,6 +23,7 @@ #include <linux/veth.h> #include "sd-netlink.h" + #include "networkd-netdev-veth.h" static int netdev_veth_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *m) { diff --git a/src/network/networkd-netdev-vxlan.c b/src/network/networkd-netdev-vxlan.c index 755ad2f934..7932b93335 100644 --- a/src/network/networkd-netdev-vxlan.c +++ b/src/network/networkd-netdev-vxlan.c @@ -22,10 +22,11 @@ #include <net/if.h> #include "sd-netlink.h" -#include "networkd-netdev-vxlan.h" -#include "networkd-link.h" + #include "conf-parser.h" #include "missing.h" +#include "networkd-link.h" +#include "networkd-netdev-vxlan.h" static int netdev_vxlan_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *m) { VxLan *v; diff --git a/src/network/networkd-netdev-vxlan.h b/src/network/networkd-netdev-vxlan.h index d21f355f5d..16977ea6a9 100644 --- a/src/network/networkd-netdev-vxlan.h +++ b/src/network/networkd-netdev-vxlan.h @@ -23,9 +23,8 @@ typedef struct VxLan VxLan; -#include "networkd-netdev.h" - #include "in-addr-util.h" +#include "networkd-netdev.h" #define VXLAN_VID_MAX (1u << 24) - 1 diff --git a/src/network/networkd-netdev.c b/src/network/networkd-netdev.c index dd0b400c6a..a86a6383da 100644 --- a/src/network/networkd-netdev.c +++ b/src/network/networkd-netdev.c @@ -411,7 +411,7 @@ int netdev_set_ifindex(NetDev *netdev, sd_netlink_message *message) { int netdev_get_mac(const char *ifname, struct ether_addr **ret) { _cleanup_free_ struct ether_addr *mac = NULL; - uint8_t result[8]; + uint64_t result; size_t l, sz; uint8_t *v; int r; @@ -438,10 +438,10 @@ int netdev_get_mac(const char *ifname, struct ether_addr **ret) { /* Let's hash the host machine ID plus the container name. We * use a fixed, but originally randomly created hash key here. */ - siphash24(result, v, sz, HASH_KEY.bytes); + result = siphash24(v, sz, HASH_KEY.bytes); assert_cc(ETH_ALEN <= sizeof(result)); - memcpy(mac->ether_addr_octet, result, ETH_ALEN); + memcpy(mac->ether_addr_octet, &result, ETH_ALEN); /* see eth_random_addr in the kernel */ mac->ether_addr_octet[0] &= 0xfe; /* clear multicast bit */ diff --git a/src/network/networkd-netdev.h b/src/network/networkd-netdev.h index 3b9ab27b67..3ab39efd57 100644 --- a/src/network/networkd-netdev.h +++ b/src/network/networkd-netdev.h @@ -26,8 +26,8 @@ typedef struct NetDev NetDev; typedef struct NetDevVTable NetDevVTable; -#include "networkd.h" #include "networkd-link.h" +#include "networkd.h" typedef struct netdev_join_callback netdev_join_callback; @@ -103,16 +103,16 @@ struct NetDev { LIST_HEAD(netdev_join_callback, callbacks); }; -#include "networkd-netdev-bridge.h" #include "networkd-netdev-bond.h" -#include "networkd-netdev-vlan.h" -#include "networkd-netdev-macvlan.h" +#include "networkd-netdev-bridge.h" +#include "networkd-netdev-dummy.h" #include "networkd-netdev-ipvlan.h" -#include "networkd-netdev-vxlan.h" -#include "networkd-netdev-veth.h" +#include "networkd-netdev-macvlan.h" #include "networkd-netdev-tunnel.h" -#include "networkd-netdev-dummy.h" #include "networkd-netdev-tuntap.h" +#include "networkd-netdev-veth.h" +#include "networkd-netdev-vlan.h" +#include "networkd-netdev-vxlan.h" struct NetDevVTable { /* How much memory does an object of this unit type need */ diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h index a27c67eea5..cb3a50d9ba 100644 --- a/src/network/networkd-network.h +++ b/src/network/networkd-network.h @@ -25,12 +25,12 @@ typedef struct Network Network; -#include "networkd.h" -#include "networkd-netdev.h" #include "networkd-address.h" -#include "networkd-route.h" #include "networkd-fdb.h" +#include "networkd-netdev.h" +#include "networkd-route.h" #include "networkd-util.h" +#include "networkd.h" #define DHCP_ROUTE_METRIC 1024 #define IPV4LL_ROUTE_METRIC 2048 diff --git a/src/network/networkd-route.c b/src/network/networkd-route.c index f4bbd06af1..ed06c21160 100644 --- a/src/network/networkd-route.c +++ b/src/network/networkd-route.c @@ -494,10 +494,18 @@ int route_configure(Route *route, Link *link, if (r < 0) return log_error_errno(r, "Could not set scope: %m"); + r = sd_rtnl_message_route_set_flags(req, route->flags); + if (r < 0) + return log_error_errno(r, "Colud not set flags: %m"); + r = sd_netlink_message_append_u32(req, RTA_PRIORITY, route->priority); if (r < 0) return log_error_errno(r, "Could not append RTA_PRIORITY attribute: %m"); + r = sd_netlink_message_append_u8(req, RTA_PREF, route->pref); + if (r < 0) + return log_error_errno(r, "Could not append RTA_PREF attribute: %m"); + r = sd_netlink_message_append_u32(req, RTA_OIF, link->ifindex); if (r < 0) return log_error_errno(r, "Could not append RTA_OIF attribute: %m"); diff --git a/src/network/networkd-route.h b/src/network/networkd-route.h index d0a51838ed..37c12907d7 100644 --- a/src/network/networkd-route.h +++ b/src/network/networkd-route.h @@ -23,8 +23,8 @@ typedef struct Route Route; -#include "networkd.h" #include "networkd-network.h" +#include "networkd.h" struct Route { Network *network; @@ -40,6 +40,8 @@ struct Route { unsigned char tos; uint32_t priority; /* note that ip(8) calls this 'metric' */ unsigned char table; + unsigned char pref; + unsigned flags; union in_addr_union gw; union in_addr_union dst; diff --git a/src/network/networkd-util.c b/src/network/networkd-util.c index df091393f6..2545621a93 100644 --- a/src/network/networkd-util.c +++ b/src/network/networkd-util.c @@ -79,10 +79,18 @@ int config_parse_address_family_boolean_with_kernel( assert(rvalue); assert(data); + /* This function is mostly obsolete now. It simply redirects + * "kernel" to "no". In older networkd versions we used to + * distuingish IPForward=off from IPForward=kernel, where the + * former would explicitly turn off forwarding while the + * latter would simply not touch the setting. But that logic + * is gone, hence silently accept the old setting, but turn it + * to "no". */ + s = address_family_boolean_from_string(rvalue); if (s < 0) { if (streq(rvalue, "kernel")) - s = _ADDRESS_FAMILY_BOOLEAN_INVALID; + s = ADDRESS_FAMILY_NO; else { log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse IPForward= option, ignoring: %s", rvalue); return 0; diff --git a/src/network/networkd.h b/src/network/networkd.h index 97665fac7a..8086e528bf 100644 --- a/src/network/networkd.h +++ b/src/network/networkd.h @@ -23,19 +23,19 @@ #include <arpa/inet.h> +#include "sd-bus.h" #include "sd-event.h" #include "sd-netlink.h" -#include "sd-bus.h" -#include "udev.h" #include "hashmap.h" #include "list.h" +#include "udev.h" typedef struct Manager Manager; -#include "networkd-network.h" #include "networkd-address-pool.h" #include "networkd-link.h" +#include "networkd-network.h" #include "networkd-util.h" struct Manager { diff --git a/src/network/test-network-tables.c b/src/network/test-network-tables.c index 438214015d..ecbbe6c3c9 100644 --- a/src/network/test-network-tables.c +++ b/src/network/test-network-tables.c @@ -1,11 +1,10 @@ -#include "networkd.h" -#include "networkd-netdev-bond.h" -#include "networkd-netdev-macvlan.h" #include "dhcp6-internal.h" #include "dhcp6-protocol.h" -#include "netlink-internal.h" #include "ethtool-util.h" - +#include "netlink-internal.h" +#include "networkd-netdev-bond.h" +#include "networkd-netdev-macvlan.h" +#include "networkd.h" #include "test-tables.h" int main(int argc, char **argv) { diff --git a/src/network/test-network.c b/src/network/test-network.c index dbed3795e3..a1a77b6867 100644 --- a/src/network/test-network.c +++ b/src/network/test-network.c @@ -20,9 +20,9 @@ ***/ #include "alloc-util.h" -#include "networkd.h" -#include "network-internal.h" #include "dhcp-lease-internal.h" +#include "network-internal.h" +#include "networkd.h" static void test_deserialize_in_addr(void) { _cleanup_free_ struct in_addr *addresses = NULL; diff --git a/src/nspawn/nspawn-cgroup.h b/src/nspawn/nspawn-cgroup.h index 985fdfaad5..4e8db63750 100644 --- a/src/nspawn/nspawn-cgroup.h +++ b/src/nspawn/nspawn-cgroup.h @@ -21,8 +21,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <sys/types.h> #include <stdbool.h> +#include <sys/types.h> int chown_cgroup(pid_t pid, uid_t uid_shift); int sync_cgroup(pid_t pid, bool unified_requested); diff --git a/src/nspawn/nspawn-expose-ports.h b/src/nspawn/nspawn-expose-ports.h index 39cec28695..cb7340bad7 100644 --- a/src/nspawn/nspawn-expose-ports.h +++ b/src/nspawn/nspawn-expose-ports.h @@ -25,8 +25,9 @@ #include "sd-event.h" #include "sd-netlink.h" -#include "list.h" + #include "in-addr-util.h" +#include "list.h" typedef struct ExposePort { int protocol; diff --git a/src/nspawn/nspawn-gperf.gperf b/src/nspawn/nspawn-gperf.gperf index b5127a387c..58f9f4c635 100644 --- a/src/nspawn/nspawn-gperf.gperf +++ b/src/nspawn/nspawn-gperf.gperf @@ -15,24 +15,25 @@ struct ConfigPerfItem; %struct-type %includes %% -Exec.Boot, config_parse_tristate, 0, offsetof(Settings, boot) -Exec.Parameters, config_parse_strv, 0, offsetof(Settings, parameters) -Exec.Environment, config_parse_strv, 0, offsetof(Settings, environment) -Exec.User, config_parse_string, 0, offsetof(Settings, user) -Exec.Capability, config_parse_capability, 0, offsetof(Settings, capability) -Exec.DropCapability, config_parse_capability, 0, offsetof(Settings, drop_capability) -Exec.KillSignal, config_parse_signal, 0, offsetof(Settings, kill_signal) -Exec.Personality, config_parse_personality, 0, offsetof(Settings, personality) -Exec.MachineID, config_parse_id128, 0, offsetof(Settings, machine_id) -Files.ReadOnly, config_parse_tristate, 0, offsetof(Settings, read_only) -Files.Volatile, config_parse_volatile_mode, 0, offsetof(Settings, volatile_mode) -Files.Bind, config_parse_bind, 0, 0 -Files.BindReadOnly, config_parse_bind, 1, 0 -Files.TemporaryFileSystem, config_parse_tmpfs, 0, 0 -Network.Private, config_parse_tristate, 0, offsetof(Settings, private_network) -Network.Interface, config_parse_strv, 0, offsetof(Settings, network_interfaces) -Network.MACVLAN, config_parse_strv, 0, offsetof(Settings, network_macvlan) -Network.IPVLAN, config_parse_strv, 0, offsetof(Settings, network_ipvlan) -Network.VirtualEthernet, config_parse_tristate, 0, offsetof(Settings, network_veth) -Network.Bridge, config_parse_string, 0, offsetof(Settings, network_bridge) -Network.Port, config_parse_expose_port, 0, 0 +Exec.Boot, config_parse_tristate, 0, offsetof(Settings, boot) +Exec.Parameters, config_parse_strv, 0, offsetof(Settings, parameters) +Exec.Environment, config_parse_strv, 0, offsetof(Settings, environment) +Exec.User, config_parse_string, 0, offsetof(Settings, user) +Exec.Capability, config_parse_capability, 0, offsetof(Settings, capability) +Exec.DropCapability, config_parse_capability, 0, offsetof(Settings, drop_capability) +Exec.KillSignal, config_parse_signal, 0, offsetof(Settings, kill_signal) +Exec.Personality, config_parse_personality, 0, offsetof(Settings, personality) +Exec.MachineID, config_parse_id128, 0, offsetof(Settings, machine_id) +Files.ReadOnly, config_parse_tristate, 0, offsetof(Settings, read_only) +Files.Volatile, config_parse_volatile_mode, 0, offsetof(Settings, volatile_mode) +Files.Bind, config_parse_bind, 0, 0 +Files.BindReadOnly, config_parse_bind, 1, 0 +Files.TemporaryFileSystem, config_parse_tmpfs, 0, 0 +Network.Private, config_parse_tristate, 0, offsetof(Settings, private_network) +Network.Interface, config_parse_strv, 0, offsetof(Settings, network_interfaces) +Network.MACVLAN, config_parse_strv, 0, offsetof(Settings, network_macvlan) +Network.IPVLAN, config_parse_strv, 0, offsetof(Settings, network_ipvlan) +Network.VirtualEthernet, config_parse_tristate, 0, offsetof(Settings, network_veth) +Network.VirtualEthernetExtra, config_parse_veth_extra, 0, 0 +Network.Bridge, config_parse_string, 0, offsetof(Settings, network_bridge) +Network.Port, config_parse_expose_port, 0, 0 diff --git a/src/nspawn/nspawn-network.c b/src/nspawn/nspawn-network.c index 29384b60b2..8f74c41c71 100644 --- a/src/nspawn/nspawn-network.c +++ b/src/nspawn/nspawn-network.c @@ -29,14 +29,16 @@ #include "alloc-util.h" #include "ether-addr-util.h" #include "netlink-util.h" +#include "nspawn-network.h" #include "siphash24.h" #include "string-util.h" #include "udev-util.h" #include "util.h" -#include "nspawn-network.h" #define HOST_HASH_KEY SD_ID128_MAKE(1a,37,6f,c7,46,ec,45,0b,ad,a3,d5,31,06,60,5d,b1) #define CONTAINER_HASH_KEY SD_ID128_MAKE(c3,c4,f9,19,b5,57,b2,1c,e6,cf,14,27,03,9c,ee,a2) +#define VETH_EXTRA_HOST_HASH_KEY SD_ID128_MAKE(48,c7,f6,b7,ea,9d,4c,9e,b7,28,d4,de,91,d5,bf,66) +#define VETH_EXTRA_CONTAINER_HASH_KEY SD_ID128_MAKE(af,50,17,61,ce,f9,4d,35,84,0d,2b,20,54,be,ce,59) #define MACVLAN_HASH_KEY SD_ID128_MAKE(00,13,6d,bc,66,83,44,81,bb,0c,f9,51,1f,24,a6,6f) static int generate_mac( @@ -45,7 +47,7 @@ static int generate_mac( sd_id128_t hash_key, uint64_t idx) { - uint8_t result[8]; + uint64_t result; size_t l, sz; uint8_t *v, *i; int r; @@ -72,10 +74,10 @@ static int generate_mac( /* Let's hash the host machine ID plus the container name. We * use a fixed, but originally randomly created hash key here. */ - siphash24(result, v, sz, hash_key.bytes); + result = htole64(siphash24(v, sz, hash_key.bytes)); assert_cc(ETH_ALEN <= sizeof(result)); - memcpy(mac->ether_addr_octet, result, ETH_ALEN); + memcpy(mac->ether_addr_octet, &result, ETH_ALEN); /* see eth_random_addr in the kernel */ mac->ether_addr_octet[0] &= 0xfe; /* clear multicast bit */ @@ -84,42 +86,32 @@ static int generate_mac( return 0; } -int setup_veth(const char *machine_name, - pid_t pid, - char iface_name[IFNAMSIZ], - bool bridge) { +static int add_veth( + sd_netlink *rtnl, + pid_t pid, + const char *ifname_host, + const struct ether_addr *mac_host, + const char *ifname_container, + const struct ether_addr *mac_container) { _cleanup_netlink_message_unref_ sd_netlink_message *m = NULL; - _cleanup_netlink_unref_ sd_netlink *rtnl = NULL; - struct ether_addr mac_host, mac_container; - int r, i; - - /* Use two different interface name prefixes depending whether - * we are in bridge mode or not. */ - snprintf(iface_name, IFNAMSIZ - 1, "%s-%s", - bridge ? "vb" : "ve", machine_name); - - r = generate_mac(machine_name, &mac_container, CONTAINER_HASH_KEY, 0); - if (r < 0) - return log_error_errno(r, "Failed to generate predictable MAC address for container side: %m"); - - r = generate_mac(machine_name, &mac_host, HOST_HASH_KEY, 0); - if (r < 0) - return log_error_errno(r, "Failed to generate predictable MAC address for host side: %m"); + int r; - r = sd_netlink_open(&rtnl); - if (r < 0) - return log_error_errno(r, "Failed to connect to netlink: %m"); + assert(rtnl); + assert(ifname_host); + assert(mac_host); + assert(ifname_container); + assert(mac_container); r = sd_rtnl_message_new_link(rtnl, &m, RTM_NEWLINK, 0); if (r < 0) return log_error_errno(r, "Failed to allocate netlink message: %m"); - r = sd_netlink_message_append_string(m, IFLA_IFNAME, iface_name); + r = sd_netlink_message_append_string(m, IFLA_IFNAME, ifname_host); if (r < 0) return log_error_errno(r, "Failed to add netlink interface name: %m"); - r = sd_netlink_message_append_ether_addr(m, IFLA_ADDRESS, &mac_host); + r = sd_netlink_message_append_ether_addr(m, IFLA_ADDRESS, mac_host); if (r < 0) return log_error_errno(r, "Failed to add netlink MAC address: %m"); @@ -135,11 +127,11 @@ int setup_veth(const char *machine_name, if (r < 0) return log_error_errno(r, "Failed to open netlink container: %m"); - r = sd_netlink_message_append_string(m, IFLA_IFNAME, "host0"); + r = sd_netlink_message_append_string(m, IFLA_IFNAME, ifname_container); if (r < 0) return log_error_errno(r, "Failed to add netlink interface name: %m"); - r = sd_netlink_message_append_ether_addr(m, IFLA_ADDRESS, &mac_container); + r = sd_netlink_message_append_ether_addr(m, IFLA_ADDRESS, mac_container); if (r < 0) return log_error_errno(r, "Failed to add netlink MAC address: %m"); @@ -161,7 +153,44 @@ int setup_veth(const char *machine_name, r = sd_netlink_call(rtnl, m, 0, NULL); if (r < 0) - return log_error_errno(r, "Failed to add new veth interfaces (host0, %s): %m", iface_name); + return log_error_errno(r, "Failed to add new veth interfaces (%s:%s): %m", ifname_host, ifname_container); + + return 0; +} + +int setup_veth(const char *machine_name, + pid_t pid, + char iface_name[IFNAMSIZ], + bool bridge) { + + _cleanup_netlink_unref_ sd_netlink *rtnl = NULL; + struct ether_addr mac_host, mac_container; + int r, i; + + assert(machine_name); + assert(pid > 0); + assert(iface_name); + + /* Use two different interface name prefixes depending whether + * we are in bridge mode or not. */ + snprintf(iface_name, IFNAMSIZ - 1, "%s-%s", + bridge ? "vb" : "ve", machine_name); + + r = generate_mac(machine_name, &mac_container, CONTAINER_HASH_KEY, 0); + if (r < 0) + return log_error_errno(r, "Failed to generate predictable MAC address for container side: %m"); + + r = generate_mac(machine_name, &mac_host, HOST_HASH_KEY, 0); + if (r < 0) + return log_error_errno(r, "Failed to generate predictable MAC address for host side: %m"); + + r = sd_netlink_open(&rtnl); + if (r < 0) + return log_error_errno(r, "Failed to connect to netlink: %m"); + + r = add_veth(rtnl, pid, iface_name, &mac_host, "host0", &mac_container); + if (r < 0) + return r; i = (int) if_nametoindex(iface_name); if (i <= 0) @@ -170,6 +199,47 @@ int setup_veth(const char *machine_name, return i; } +int setup_veth_extra( + const char *machine_name, + pid_t pid, + char **pairs) { + + _cleanup_netlink_unref_ sd_netlink *rtnl = NULL; + uint64_t idx = 0; + char **a, **b; + int r; + + assert(machine_name); + assert(pid > 0); + + if (strv_isempty(pairs)) + return 0; + + r = sd_netlink_open(&rtnl); + if (r < 0) + return log_error_errno(r, "Failed to connect to netlink: %m"); + + STRV_FOREACH_PAIR(a, b, pairs) { + struct ether_addr mac_host, mac_container; + + r = generate_mac(machine_name, &mac_container, VETH_EXTRA_CONTAINER_HASH_KEY, idx); + if (r < 0) + return log_error_errno(r, "Failed to generate predictable MAC address for container side of extra veth link: %m"); + + r = generate_mac(machine_name, &mac_host, VETH_EXTRA_HOST_HASH_KEY, idx); + if (r < 0) + return log_error_errno(r, "Failed to generate predictable MAC address for container side of extra veth link: %m"); + + r = add_veth(rtnl, pid, *a, &mac_host, *b, &mac_container); + if (r < 0) + return r; + + idx ++; + } + + return 0; +} + int setup_bridge(const char *veth_name, const char *bridge_name) { _cleanup_netlink_message_unref_ sd_netlink_message *m = NULL; _cleanup_netlink_unref_ sd_netlink *rtnl = NULL; @@ -439,3 +509,34 @@ int setup_ipvlan(const char *machine_name, pid_t pid, char **ifaces) { return 0; } + +int veth_extra_parse(char ***l, const char *p) { + _cleanup_free_ char *a = NULL, *b = NULL; + int r; + + r = extract_first_word(&p, &a, ":", EXTRACT_DONT_COALESCE_SEPARATORS); + if (r < 0) + return r; + if (r == 0 || isempty(a)) + return -EINVAL; + + r = extract_first_word(&p, &b, ":", EXTRACT_DONT_COALESCE_SEPARATORS); + if (r < 0) + return r; + if (r == 0 || isempty(b)) { + free(b); + b = strdup(a); + if (!b) + return -ENOMEM; + } + + if (p) + return -EINVAL; + + r = strv_push_pair(l, a, b); + if (r < 0) + return -ENOMEM; + + a = b = NULL; + return 0; +} diff --git a/src/nspawn/nspawn-network.h b/src/nspawn/nspawn-network.h index 311e6d06cb..c91fc79c42 100644 --- a/src/nspawn/nspawn-network.h +++ b/src/nspawn/nspawn-network.h @@ -22,11 +22,11 @@ ***/ #include <net/if.h> - -#include <sys/types.h> #include <stdbool.h> +#include <sys/types.h> int setup_veth(const char *machine_name, pid_t pid, char iface_name[IFNAMSIZ], bool bridge); +int setup_veth_extra(const char *machine_name, pid_t pid, char **pairs); int setup_bridge(const char *veth_name, const char *bridge_name); @@ -34,3 +34,5 @@ int setup_macvlan(const char *machine_name, pid_t pid, char **ifaces); int setup_ipvlan(const char *machine_name, pid_t pid, char **ifaces); int move_network_interfaces(pid_t pid, char **ifaces); + +int veth_extra_parse(char ***l, const char *p); diff --git a/src/nspawn/nspawn-register.c b/src/nspawn/nspawn-register.c index 374f958c20..50871464c5 100644 --- a/src/nspawn/nspawn-register.c +++ b/src/nspawn/nspawn-register.c @@ -105,6 +105,10 @@ int register_machine( return bus_log_create_error(r); } + r = sd_bus_message_append(m, "(sv)", "TasksMax", "t", 8192); + if (r < 0) + return bus_log_create_error(r); + r = sd_bus_message_append(m, "(sv)", "DevicePolicy", "s", "strict"); if (r < 0) return bus_log_create_error(r); diff --git a/src/nspawn/nspawn-settings.c b/src/nspawn/nspawn-settings.c index 6885d0641e..d6b64d8d5a 100644 --- a/src/nspawn/nspawn-settings.c +++ b/src/nspawn/nspawn-settings.c @@ -22,6 +22,7 @@ #include "alloc-util.h" #include "cap-list.h" #include "conf-parser.h" +#include "nspawn-network.h" #include "nspawn-settings.h" #include "process-util.h" #include "strv.h" @@ -77,6 +78,7 @@ Settings* settings_free(Settings *s) { strv_free(s->network_interfaces); strv_free(s->network_macvlan); strv_free(s->network_ipvlan); + strv_free(s->network_veth_extra); free(s->network_bridge); expose_port_free_all(s->expose_ports); @@ -95,7 +97,8 @@ bool settings_private_network(Settings *s) { s->network_bridge || s->network_interfaces || s->network_macvlan || - s->network_ipvlan; + s->network_ipvlan || + s->network_veth_extra; } bool settings_network_veth(Settings *s) { @@ -269,15 +272,33 @@ int config_parse_tmpfs( return 0; } - if (settings->network_bridge) - settings->network_veth = true; + return 0; +} - if (settings->network_interfaces || - settings->network_macvlan || - settings->network_ipvlan || - settings->network_bridge || - settings->network_veth) - settings->private_network = true; +int config_parse_veth_extra( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + Settings *settings = data; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + + r = veth_extra_parse(&settings->network_veth_extra, rvalue); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Invalid extra virtual Ethernet link specification %s: %m", rvalue); + return 0; + } return 0; } diff --git a/src/nspawn/nspawn-settings.h b/src/nspawn/nspawn-settings.h index 16e8c54508..10230a5b83 100644 --- a/src/nspawn/nspawn-settings.h +++ b/src/nspawn/nspawn-settings.h @@ -24,9 +24,8 @@ #include <stdio.h> #include "macro.h" - -#include "nspawn-mount.h" #include "nspawn-expose-ports.h" +#include "nspawn-mount.h" typedef enum SettingsMask { SETTING_BOOT = 1 << 0, @@ -69,6 +68,7 @@ typedef struct Settings { char **network_interfaces; char **network_macvlan; char **network_ipvlan; + char **network_veth_extra; ExposePort *expose_ports; } Settings; @@ -88,3 +88,4 @@ int config_parse_expose_port(const char *unit, const char *filename, unsigned li int config_parse_volatile_mode(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_bind(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_tmpfs(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_veth_extra(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index 4c48681f17..f6a2c0386e 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -165,6 +165,7 @@ static char **arg_network_interfaces = NULL; static char **arg_network_macvlan = NULL; static char **arg_network_ipvlan = NULL; static bool arg_network_veth = false; +static char **arg_network_veth_extra = NULL; static char *arg_network_bridge = NULL; static unsigned long arg_personality = PERSONALITY_INVALID; static char *arg_image = NULL; @@ -212,6 +213,9 @@ static void help(void) { " existing network interface to the container\n" " -n --network-veth Add a virtual Ethernet connection between host\n" " and container\n" + " --network-veth-extra=HOSTIF[:CONTAINERIF]\n" + " Add an additional virtual Ethernet link between\n" + " host and container\n" " --network-bridge=INTERFACE\n" " Add a virtual Ethernet connection between host\n" " and container and add it to an existing bridge on\n" @@ -334,6 +338,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_NETWORK_MACVLAN, ARG_NETWORK_IPVLAN, ARG_NETWORK_BRIDGE, + ARG_NETWORK_VETH_EXTRA, ARG_PERSONALITY, ARG_VOLATILE, ARG_TEMPLATE, @@ -375,6 +380,7 @@ static int parse_argv(int argc, char *argv[]) { { "network-macvlan", required_argument, NULL, ARG_NETWORK_MACVLAN }, { "network-ipvlan", required_argument, NULL, ARG_NETWORK_IPVLAN }, { "network-veth", no_argument, NULL, 'n' }, + { "network-veth-extra", required_argument, NULL, ARG_NETWORK_VETH_EXTRA}, { "network-bridge", required_argument, NULL, ARG_NETWORK_BRIDGE }, { "personality", required_argument, NULL, ARG_PERSONALITY }, { "image", required_argument, NULL, 'i' }, @@ -449,6 +455,15 @@ static int parse_argv(int argc, char *argv[]) { arg_settings_mask |= SETTING_NETWORK; break; + case ARG_NETWORK_VETH_EXTRA: + r = veth_extra_parse(&arg_network_veth_extra, optarg); + if (r < 0) + return log_error_errno(r, "Failed to parse --network-veth-extra= parameter: %s", optarg); + + arg_private_network = true; + arg_settings_mask |= SETTING_NETWORK; + break; + case ARG_NETWORK_INTERFACE: if (strv_extend(&arg_network_interfaces, optarg) < 0) return log_oom(); @@ -2268,7 +2283,7 @@ static int wait_for_container(pid_t pid, ContainerStatus *container) { static int on_orderly_shutdown(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) { pid_t pid; - pid = PTR_TO_UINT32(userdata); + pid = PTR_TO_PID(userdata); if (pid > 0) { if (kill(pid, arg_kill_signal) >= 0) { log_info("Trying to halt container. Send SIGTERM again to trigger immediate termination."); @@ -2964,12 +2979,13 @@ static int load_settings(void) { settings->network_bridge || settings->network_interfaces || settings->network_macvlan || - settings->network_ipvlan)) { + settings->network_ipvlan || + settings->network_veth_extra)) { if (!arg_settings_trusted) log_warning("Ignoring network settings, file %s is not trusted.", p); else { - arg_network_veth = settings_private_network(settings); + arg_network_veth = settings_network_veth(settings); arg_private_network = settings_private_network(settings); strv_free(arg_network_interfaces); @@ -2984,6 +3000,10 @@ static int load_settings(void) { arg_network_ipvlan = settings->network_ipvlan; settings->network_ipvlan = NULL; + strv_free(arg_network_veth_extra); + arg_network_veth_extra = settings->network_veth_extra; + settings->network_veth_extra = NULL; + free(arg_network_bridge); arg_network_bridge = settings->network_bridge; settings->network_bridge = NULL; @@ -3409,6 +3429,10 @@ int main(int argc, char *argv[]) { } } + r = setup_veth_extra(arg_machine, pid, arg_network_veth_extra); + if (r < 0) + goto finish; + r = setup_macvlan(arg_machine, pid, arg_network_macvlan); if (r < 0) goto finish; @@ -3486,8 +3510,8 @@ int main(int argc, char *argv[]) { if (arg_kill_signal > 0) { /* Try to kill the init system on SIGINT or SIGTERM */ - sd_event_add_signal(event, NULL, SIGINT, on_orderly_shutdown, UINT32_TO_PTR(pid)); - sd_event_add_signal(event, NULL, SIGTERM, on_orderly_shutdown, UINT32_TO_PTR(pid)); + sd_event_add_signal(event, NULL, SIGINT, on_orderly_shutdown, PID_TO_PTR(pid)); + sd_event_add_signal(event, NULL, SIGTERM, on_orderly_shutdown, PID_TO_PTR(pid)); } else { /* Immediately exit */ sd_event_add_signal(event, NULL, SIGINT, NULL, NULL); @@ -3610,6 +3634,7 @@ finish: strv_free(arg_network_interfaces); strv_free(arg_network_macvlan); strv_free(arg_network_ipvlan); + strv_free(arg_network_veth_extra); strv_free(arg_parameters); custom_mount_free_all(arg_custom_mounts, arg_n_custom_mounts); expose_port_free_all(arg_expose_ports); diff --git a/src/nss-mymachines/nss-mymachines.c b/src/nss-mymachines/nss-mymachines.c index 969fa9619e..c98a959b3b 100644 --- a/src/nss-mymachines/nss-mymachines.c +++ b/src/nss-mymachines/nss-mymachines.c @@ -416,6 +416,9 @@ enum nss_status _nss_mymachines_getpwnam_r( if (!e || e == p) goto not_found; + if (e - p > HOST_NAME_MAX - 1) /* -1 for the last dash */ + goto not_found; + r = parse_uid(e + 1, &uid); if (r < 0) goto not_found; @@ -573,6 +576,9 @@ enum nss_status _nss_mymachines_getgrnam_r( if (!e || e == p) goto not_found; + if (e - p > HOST_NAME_MAX - 1) /* -1 for the last dash */ + goto not_found; + r = parse_gid(e + 1, &gid); if (r < 0) goto not_found; diff --git a/src/quotacheck/quotacheck.c b/src/quotacheck/quotacheck.c index dc2911e4e8..883d96608d 100644 --- a/src/quotacheck/quotacheck.c +++ b/src/quotacheck/quotacheck.c @@ -25,11 +25,11 @@ #include <sys/prctl.h> #include <unistd.h> +#include "proc-cmdline.h" #include "process-util.h" #include "signal-util.h" #include "string-util.h" #include "util.h" -#include "proc-cmdline.h" static bool arg_skip = false; static bool arg_force = false; diff --git a/src/remount-fs/remount-fs.c b/src/remount-fs/remount-fs.c index 57f99c9ef0..9fc56284d2 100644 --- a/src/remount-fs/remount-fs.c +++ b/src/remount-fs/remount-fs.c @@ -19,19 +19,22 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <unistd.h> #include <errno.h> +#include <mntent.h> #include <string.h> +#include <sys/prctl.h> #include <sys/stat.h> #include <sys/wait.h> -#include <mntent.h> +#include <unistd.h> #include "exit-status.h" #include "log.h" #include "mount-setup.h" #include "mount-util.h" #include "path-util.h" +#include "process-util.h" #include "signal-util.h" +#include "strv.h" #include "util.h" /* Goes through /etc/fstab and remounts all API file systems, applying @@ -39,10 +42,10 @@ * respected */ int main(int argc, char *argv[]) { - int ret = EXIT_FAILURE; + _cleanup_hashmap_free_free_ Hashmap *pids = NULL; _cleanup_endmntent_ FILE *f = NULL; struct mntent* me; - Hashmap *pids = NULL; + int r; if (argc > 1) { log_error("This program takes no argument."); @@ -57,21 +60,21 @@ int main(int argc, char *argv[]) { f = setmntent("/etc/fstab", "r"); if (!f) { - if (errno == ENOENT) - return EXIT_SUCCESS; + if (errno == ENOENT) { + r = 0; + goto finish; + } - log_error_errno(errno, "Failed to open /etc/fstab: %m"); - return EXIT_FAILURE; + r = log_error_errno(errno, "Failed to open /etc/fstab: %m"); + goto finish; } pids = hashmap_new(NULL); if (!pids) { - log_error("Failed to allocate set"); + r = log_oom(); goto finish; } - ret = EXIT_SUCCESS; - while ((me = getmntent(f))) { pid_t pid; int k; @@ -87,25 +90,18 @@ int main(int argc, char *argv[]) { pid = fork(); if (pid < 0) { - log_error_errno(errno, "Failed to fork: %m"); - ret = EXIT_FAILURE; - continue; + r = log_error_errno(errno, "Failed to fork: %m"); + goto finish; } if (pid == 0) { - const char *arguments[5]; /* Child */ (void) reset_all_signal_handlers(); (void) reset_signal_mask(); + (void) prctl(PR_SET_PDEATHSIG, SIGTERM); - arguments[0] = MOUNT_PATH; - arguments[1] = me->mnt_dir; - arguments[2] = "-o"; - arguments[3] = "remount"; - arguments[4] = NULL; - - execv(MOUNT_PATH, (char **) arguments); + execv(MOUNT_PATH, STRV_MAKE(MOUNT_PATH, me->mnt_dir, "-o", "remount")); log_error_errno(errno, "Failed to execute " MOUNT_PATH ": %m"); _exit(EXIT_FAILURE); @@ -115,20 +111,19 @@ int main(int argc, char *argv[]) { s = strdup(me->mnt_dir); if (!s) { - log_oom(); - ret = EXIT_FAILURE; - continue; + r = log_oom(); + goto finish; } - - k = hashmap_put(pids, UINT_TO_PTR(pid), s); + k = hashmap_put(pids, PID_TO_PTR(pid), s); if (k < 0) { - log_error_errno(k, "Failed to add PID to set: %m"); - ret = EXIT_FAILURE; - continue; + free(s); + r = log_oom(); + goto finish; } } + r = 0; while (!hashmap_isempty(pids)) { siginfo_t si = {}; char *s; @@ -138,12 +133,11 @@ int main(int argc, char *argv[]) { if (errno == EINTR) continue; - log_error_errno(errno, "waitid() failed: %m"); - ret = EXIT_FAILURE; - break; + r = log_error_errno(errno, "waitid() failed: %m"); + goto finish; } - s = hashmap_remove(pids, UINT_TO_PTR(si.si_pid)); + s = hashmap_remove(pids, PID_TO_PTR(si.si_pid)); if (s) { if (!is_clean_exit(si.si_code, si.si_status, NULL)) { if (si.si_code == CLD_EXITED) @@ -151,7 +145,7 @@ int main(int argc, char *argv[]) { else log_error(MOUNT_PATH " for %s terminated by signal %s.", s, signal_to_string(si.si_status)); - ret = EXIT_FAILURE; + r = -ENOEXEC; } free(s); @@ -159,9 +153,5 @@ int main(int argc, char *argv[]) { } finish: - - if (pids) - hashmap_free_free(pids); - - return ret; + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; } diff --git a/src/resolve-host/resolve-host.c b/src/resolve-host/resolve-host.c index 432e62dd9f..f68751a2e5 100644 --- a/src/resolve-host/resolve-host.c +++ b/src/resolve-host/resolve-host.c @@ -19,8 +19,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <net/if.h> #include <getopt.h> +#include <net/if.h> #include "sd-bus.h" @@ -28,6 +28,7 @@ #include "alloc-util.h" #include "bus-error.h" #include "bus-util.h" +#include "escape.h" #include "in-addr-util.h" #include "parse-util.h" #include "resolved-def.h" @@ -41,6 +42,7 @@ static int arg_type = 0; static uint16_t arg_class = 0; static bool arg_legend = true; static uint64_t arg_flags = 0; +static bool arg_resolve_service = false; static void print_source(uint64_t flags, usec_t rtt) { char rtt_str[FORMAT_TIMESTAMP_MAX]; @@ -102,10 +104,8 @@ static int resolve_host(sd_bus *bus, const char *name) { ts = now(CLOCK_MONOTONIC); r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply); - if (r < 0) { - log_error("%s: resolve call failed: %s", name, bus_error_message(&error, r)); - return r; - } + if (r < 0) + return log_error_errno(r, "%s: resolve call failed: %s", name, bus_error_message(&error, r)); ts = now(CLOCK_MONOTONIC) - ts; @@ -114,10 +114,10 @@ static int resolve_host(sd_bus *bus, const char *name) { return bus_log_parse_error(r); while ((r = sd_bus_message_enter_container(reply, 'r', "iiay")) > 0) { - const void *a; - size_t sz; _cleanup_free_ char *pretty = NULL; int ifindex, family; + const void *a; + size_t sz; assert_cc(sizeof(int) == sizeof(int32_t)); @@ -140,7 +140,7 @@ static int resolve_host(sd_bus *bus, const char *name) { if (sz != FAMILY_ADDRESS_SIZE(family)) { log_error("%s: systemd-resolved returned address of invalid size %zu for family %s", name, sz, af_to_name(family) ?: "unknown"); - continue; + return -EINVAL; } ifname[0] = 0; @@ -437,6 +437,207 @@ static int resolve_record(sd_bus *bus, const char *name) { return 0; } +static int resolve_service(sd_bus *bus, const char *name, const char *type, const char *domain) { + const char *canonical_name, *canonical_type, *canonical_domain; + _cleanup_bus_message_unref_ sd_bus_message *req = NULL, *reply = NULL; + _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; + char ifname[IF_NAMESIZE] = ""; + size_t indent, sz; + uint64_t flags; + const char *p; + unsigned c; + usec_t ts; + int r; + + assert(bus); + assert(domain); + + if (isempty(name)) + name = NULL; + if (isempty(type)) + type = NULL; + + if (arg_ifindex > 0 && !if_indextoname(arg_ifindex, ifname)) + return log_error_errno(errno, "Failed to resolve interface name for index %i: %m", arg_ifindex); + + if (name) + log_debug("Resolving service \"%s\" of type %s in %s (family %s, interface %s).", name, type, domain, af_to_name(arg_family) ?: "*", isempty(ifname) ? "*" : ifname); + else if (type) + log_debug("Resolving service type %s of %s (family %s, interface %s).", type, domain, af_to_name(arg_family) ?: "*", isempty(ifname) ? "*" : ifname); + else + log_debug("Resolving service type %s (family %s, interface %s).", domain, af_to_name(arg_family) ?: "*", isempty(ifname) ? "*" : ifname); + + r = sd_bus_message_new_method_call( + bus, + &req, + "org.freedesktop.resolve1", + "/org/freedesktop/resolve1", + "org.freedesktop.resolve1.Manager", + "ResolveService"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append(req, "isssit", arg_ifindex, name, type, domain, arg_family, arg_flags); + if (r < 0) + return bus_log_create_error(r); + + ts = now(CLOCK_MONOTONIC); + + r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply); + if (r < 0) + return log_error_errno(r, "Resolve call failed: %s", bus_error_message(&error, r)); + + ts = now(CLOCK_MONOTONIC) - ts; + + r = sd_bus_message_enter_container(reply, 'a', "(qqqsa(iiay)s)"); + if (r < 0) + return bus_log_parse_error(r); + + indent = + (name ? strlen(name) + 1 : 0) + + (type ? strlen(type) + 1 : 0) + + strlen(domain) + 2; + + c = 0; + while ((r = sd_bus_message_enter_container(reply, 'r', "qqqsa(iiay)s")) > 0) { + uint16_t priority, weight, port; + const char *hostname, *canonical; + + r = sd_bus_message_read(reply, "qqqs", &priority, &weight, &port, &hostname); + if (r < 0) + return bus_log_parse_error(r); + + if (name) + printf("%*s%s", (int) strlen(name), c == 0 ? name : "", c == 0 ? "/" : " "); + if (type) + printf("%*s%s", (int) strlen(type), c == 0 ? type : "", c == 0 ? "/" : " "); + + printf("%*s%s %s:%u [priority=%u, weight=%u]\n", + (int) strlen(domain), c == 0 ? domain : "", + c == 0 ? ":" : " ", + hostname, port, + priority, weight); + + r = sd_bus_message_enter_container(reply, 'a', "(iiay)"); + if (r < 0) + return bus_log_parse_error(r); + + while ((r = sd_bus_message_enter_container(reply, 'r', "iiay")) > 0) { + _cleanup_free_ char *pretty = NULL; + int ifindex, family; + const void *a; + + assert_cc(sizeof(int) == sizeof(int32_t)); + + r = sd_bus_message_read(reply, "ii", &ifindex, &family); + if (r < 0) + return bus_log_parse_error(r); + + r = sd_bus_message_read_array(reply, 'y', &a, &sz); + if (r < 0) + return bus_log_parse_error(r); + + r = sd_bus_message_exit_container(reply); + if (r < 0) + return bus_log_parse_error(r); + + if (!IN_SET(family, AF_INET, AF_INET6)) { + log_debug("%s: skipping entry with family %d (%s)", name, family, af_to_name(family) ?: "unknown"); + continue; + } + + if (sz != FAMILY_ADDRESS_SIZE(family)) { + log_error("%s: systemd-resolved returned address of invalid size %zu for family %s", name, sz, af_to_name(family) ?: "unknown"); + return -EINVAL; + } + + ifname[0] = 0; + if (ifindex > 0 && !if_indextoname(ifindex, ifname)) + log_warning_errno(errno, "Failed to resolve interface name for index %i: %m", ifindex); + + r = in_addr_to_string(family, a, &pretty); + if (r < 0) + return log_error_errno(r, "Failed to print address for %s: %m", name); + + printf("%*s%s%s%s\n", (int) indent, "", pretty, isempty(ifname) ? "" : "%s", ifname); + } + if (r < 0) + return bus_log_parse_error(r); + + r = sd_bus_message_exit_container(reply); + if (r < 0) + return bus_log_parse_error(r); + + r = sd_bus_message_read(reply, "s", &canonical); + if (r < 0) + return bus_log_parse_error(r); + + if (!streq(hostname, canonical)) + printf("%*s(%s)\n", (int) indent, "", canonical); + + r = sd_bus_message_exit_container(reply); + if (r < 0) + return bus_log_parse_error(r); + + c++; + } + if (r < 0) + return bus_log_parse_error(r); + + r = sd_bus_message_exit_container(reply); + if (r < 0) + return bus_log_parse_error(r); + + r = sd_bus_message_enter_container(reply, 'a', "ay"); + if (r < 0) + return bus_log_parse_error(r); + + c = 0; + while ((r = sd_bus_message_read_array(reply, 'y', (const void**) &p, &sz)) > 0) { + _cleanup_free_ char *escaped = NULL; + + escaped = cescape_length(p, sz); + if (!escaped) + return log_oom(); + + printf("%*s%s\n", (int) indent, "", escaped); + c++; + } + if (r < 0) + return bus_log_parse_error(r); + + r = sd_bus_message_exit_container(reply); + if (r < 0) + return bus_log_parse_error(r); + + r = sd_bus_message_read(reply, "ssst", &canonical_name, &canonical_type, &canonical_domain, &flags); + if (r < 0) + return bus_log_parse_error(r); + + if (isempty(canonical_name)) + canonical_name = NULL; + if (isempty(canonical_type)) + canonical_type = NULL; + + if (!streq_ptr(name, canonical_name) || + !streq_ptr(type, canonical_type) || + !streq_ptr(domain, canonical_domain)) { + + printf("%*s(", (int) indent, ""); + + if (canonical_name) + printf("%s/", canonical_name); + if (canonical_type) + printf("%s/", canonical_type); + + printf("%s)\n", canonical_domain); + } + + print_source(flags, ts); + + return 0; +} + static void help_dns_types(void) { int i; const char *t; @@ -464,33 +665,49 @@ static void help_dns_classes(void) { } static void help(void) { - printf("%s [OPTIONS...]\n\n" - "Resolve IPv4 or IPv6 addresses.\n\n" - " -h --help Show this help\n" - " --version Show package version\n" - " -4 Resolve IPv4 addresses\n" - " -6 Resolve IPv6 addresses\n" - " -i INTERFACE Look on interface\n" - " -p --protocol=PROTOCOL Look via protocol\n" - " -t --type=TYPE Query RR with DNS type\n" - " -c --class=CLASS Query RR with DNS class\n" - " --legend[=BOOL] Do [not] print column headers\n" - , program_invocation_short_name); + printf("%s [OPTIONS...] NAME...\n" + "%s [OPTIONS...] --service [[NAME] TYPE] DOMAIN\n\n" + "Resolve domain names, IPv4 or IPv6 addresses, resource records, and services.\n\n" + " -h --help Show this help\n" + " --version Show package version\n" + " -4 Resolve IPv4 addresses\n" + " -6 Resolve IPv6 addresses\n" + " -i INTERFACE Look on interface\n" + " -p --protocol=PROTOCOL Look via protocol\n" + " -t --type=TYPE Query RR with DNS type\n" + " -c --class=CLASS Query RR with DNS class\n" + " --service Resolve service (SRV)\n" + " --service-address=BOOL Do [not] resolve address for services\n" + " --service-txt=BOOL Do [not] resolve TXT records for services\n" + " --cname=BOOL Do [not] follow CNAME redirects\n" + " --search=BOOL Do [not] use search domains\n" + " --legend=BOOL Do [not] print column headers\n" + , program_invocation_short_name, program_invocation_short_name); } static int parse_argv(int argc, char *argv[]) { enum { ARG_VERSION = 0x100, ARG_LEGEND, + ARG_SERVICE, + ARG_CNAME, + ARG_SERVICE_ADDRESS, + ARG_SERVICE_TXT, + ARG_SEARCH, }; static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "type", required_argument, NULL, 't' }, - { "class", required_argument, NULL, 'c' }, - { "legend", optional_argument, NULL, ARG_LEGEND }, - { "protocol", required_argument, NULL, 'p' }, + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "type", required_argument, NULL, 't' }, + { "class", required_argument, NULL, 'c' }, + { "legend", required_argument, NULL, ARG_LEGEND }, + { "protocol", required_argument, NULL, 'p' }, + { "cname", required_argument, NULL, ARG_CNAME }, + { "service", no_argument, NULL, ARG_SERVICE }, + { "service-address", required_argument, NULL, ARG_SERVICE_ADDRESS }, + { "service-txt", required_argument, NULL, ARG_SERVICE_TXT }, + { "search", required_argument, NULL, ARG_SEARCH }, {} }; @@ -563,16 +780,11 @@ static int parse_argv(int argc, char *argv[]) { break; case ARG_LEGEND: - if (optarg) { - r = parse_boolean(optarg); - if (r < 0) { - log_error("Failed to parse --legend= argument"); - return r; - } - - arg_legend = !!r; - } else - arg_legend = false; + r = parse_boolean(optarg); + if (r < 0) + return log_error_errno(r, "Failed to parse --legend= argument"); + + arg_legend = r; break; case 'p': @@ -591,6 +803,50 @@ static int parse_argv(int argc, char *argv[]) { break; + case ARG_SERVICE: + arg_resolve_service = true; + break; + + case ARG_CNAME: + r = parse_boolean(optarg); + if (r < 0) + return log_error_errno(r, "Failed to parse --cname= argument."); + if (r == 0) + arg_flags |= SD_RESOLVED_NO_CNAME; + else + arg_flags &= ~SD_RESOLVED_NO_CNAME; + break; + + case ARG_SERVICE_ADDRESS: + r = parse_boolean(optarg); + if (r < 0) + return log_error_errno(r, "Failed to parse --service-address= argument."); + if (r == 0) + arg_flags |= SD_RESOLVED_NO_ADDRESS; + else + arg_flags &= ~SD_RESOLVED_NO_ADDRESS; + break; + + case ARG_SERVICE_TXT: + r = parse_boolean(optarg); + if (r < 0) + return log_error_errno(r, "Failed to parse --service-txt= argument."); + if (r == 0) + arg_flags |= SD_RESOLVED_NO_TXT; + else + arg_flags &= ~SD_RESOLVED_NO_TXT; + break; + + case ARG_SEARCH: + r = parse_boolean(optarg); + if (r < 0) + return log_error_errno(r, "Failed to parse --search argument."); + if (r == 0) + arg_flags |= SD_RESOLVED_NO_SEARCH; + else + arg_flags &= ~SD_RESOLVED_NO_SEARCH; + break; + case '?': return -EINVAL; @@ -599,7 +855,12 @@ static int parse_argv(int argc, char *argv[]) { } if (arg_type == 0 && arg_class != 0) { - log_error("--class= may only be used in conjunction with --type="); + log_error("--class= may only be used in conjunction with --type=."); + return -EINVAL; + } + + if (arg_type != 0 && arg_resolve_service) { + log_error("--service and --type= may not be combined."); return -EINVAL; } @@ -632,6 +893,28 @@ int main(int argc, char **argv) { goto finish; } + if (arg_resolve_service) { + + if (argc < optind + 1) { + log_error("Domain specification required."); + r = -EINVAL; + goto finish; + + } else if (argc == optind + 1) + r = resolve_service(bus, NULL, NULL, argv[optind]); + else if (argc == optind + 2) + r = resolve_service(bus, NULL, argv[optind], argv[optind+1]); + else if (argc == optind + 3) + r = resolve_service(bus, argv[optind], argv[optind+1], argv[optind+2]); + else { + log_error("Too many arguments"); + r = -EINVAL; + goto finish; + } + + goto finish; + } + while (argv[optind]) { int family, ifindex, k; union in_addr_union a; diff --git a/src/resolve/resolved-bus.c b/src/resolve/resolved-bus.c index f0a3b607d4..62bb08a2e8 100644 --- a/src/resolve/resolved-bus.c +++ b/src/resolve/resolved-bus.c @@ -31,15 +31,14 @@ static int reply_query_state(DnsQuery *q) { const char *name; int r; - if (q->request_hostname) - name = q->request_hostname; - else { + if (q->request_address_valid) { r = in_addr_to_string(q->request_family, &q->request_address, &ip); if (r < 0) return r; name = ip; - } + } else + name = dns_question_first_name(q->question); switch (q->state) { @@ -132,10 +131,9 @@ static int append_address(sd_bus_message *reply, DnsResourceRecord *rr, int ifin } static void bus_method_resolve_hostname_complete(DnsQuery *q) { - _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *cname = NULL, *canonical = NULL; + _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *canonical = NULL; _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; - _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL; - unsigned added = 0, i; + unsigned added = 0; int r; assert(q); @@ -145,6 +143,16 @@ static void bus_method_resolve_hostname_complete(DnsQuery *q) { goto finish; } + r = dns_query_process_cname(q); + if (r == -ELOOP) { + r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_CNAME_LOOP, "CNAME loop detected, or CNAME resolving disabled on '%s'", dns_question_first_name(q->question)); + goto finish; + } + if (r < 0) + goto finish; + if (r > 0) /* This was a cname, and the query was restarted. */ + return; + r = sd_bus_message_new_method_return(q->request, &reply); if (r < 0) goto finish; @@ -154,92 +162,42 @@ static void bus_method_resolve_hostname_complete(DnsQuery *q) { goto finish; if (q->answer) { - answer = dns_answer_ref(q->answer); + DnsResourceRecord *rr; + int ifindex; - for (i = 0; i < answer->n_rrs; i++) { - r = dns_question_matches_rr(q->question, answer->items[i].rr); + DNS_ANSWER_FOREACH_IFINDEX(rr, ifindex, q->answer) { + r = dns_question_matches_rr(q->question, rr, DNS_SEARCH_DOMAIN_NAME(q->answer_search_domain)); if (r < 0) goto finish; - if (r == 0) { - /* Hmm, if this is not an address record, - maybe it's a cname? If so, remember this */ - r = dns_question_matches_cname(q->question, answer->items[i].rr); - if (r < 0) - goto finish; - if (r > 0) - cname = dns_resource_record_ref(answer->items[i].rr); - + if (r == 0) continue; - } - r = append_address(reply, answer->items[i].rr, answer->items[i].ifindex); + r = append_address(reply, rr, ifindex); if (r < 0) goto finish; if (!canonical) - canonical = dns_resource_record_ref(answer->items[i].rr); + canonical = dns_resource_record_ref(rr); added ++; } } - if (added == 0) { - if (!cname) { - r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "'%s' does not have any RR of requested type", q->request_hostname); - goto finish; - } - - /* This has a cname? Then update the query with the - * new cname. */ - r = dns_query_cname_redirect(q, cname); - if (r < 0) { - if (r == -ELOOP) - r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_CNAME_LOOP, "CNAME loop on '%s'", q->request_hostname); - else - r = sd_bus_reply_method_errno(q->request, -r, NULL); - - goto finish; - } - - /* Before we restart the query, let's see if any of - * the RRs we already got already answers our query */ - for (i = 0; i < answer->n_rrs; i++) { - r = dns_question_matches_rr(q->question, answer->items[i].rr); - if (r < 0) - goto finish; - if (r == 0) - continue; - - r = append_address(reply, answer->items[i].rr, answer->items[i].ifindex); - if (r < 0) - goto finish; - - if (!canonical) - canonical = dns_resource_record_ref(answer->items[i].rr); - - added++; - } - - /* If we didn't find anything, then let's restart the - * query, this time with the cname */ - if (added <= 0) { - r = dns_query_go(q); - if (r < 0) { - r = sd_bus_reply_method_errno(q->request, -r, NULL); - goto finish; - } - - return; - } + if (added <= 0) { + r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "'%s' does not have any RR of the requested type", dns_question_first_name(q->question)); + goto finish; } r = sd_bus_message_close_container(reply); if (r < 0) goto finish; - /* Return the precise spelling and uppercasing reported by the server */ + /* Return the precise spelling and uppercasing and CNAME target reported by the server */ assert(canonical); - r = sd_bus_message_append(reply, "st", DNS_RESOURCE_KEY_NAME(canonical->key), SD_RESOLVED_FLAGS_MAKE(q->answer_protocol, q->answer_family)); + r = sd_bus_message_append( + reply, "st", + DNS_RESOURCE_KEY_NAME(canonical->key), + SD_RESOLVED_FLAGS_MAKE(q->answer_protocol, q->answer_family)); if (r < 0) goto finish; @@ -248,23 +206,23 @@ static void bus_method_resolve_hostname_complete(DnsQuery *q) { finish: if (r < 0) { log_error_errno(r, "Failed to send hostname reply: %m"); - sd_bus_reply_method_errno(q->request, -r, NULL); + sd_bus_reply_method_errno(q->request, r, NULL); } dns_query_free(q); } -static int check_ifindex_flags(int ifindex, uint64_t *flags, sd_bus_error *error) { +static int check_ifindex_flags(int ifindex, uint64_t *flags, uint64_t ok, sd_bus_error *error) { assert(flags); if (ifindex < 0) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid interface index"); - if (*flags & ~SD_RESOLVED_FLAGS_ALL) + if (*flags & ~(SD_RESOLVED_PROTOCOLS_ALL|SD_RESOLVED_NO_CNAME|ok)) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid flags parameter"); - if (*flags == 0) - *flags = SD_RESOLVED_FLAGS_DEFAULT; + if ((*flags & SD_RESOLVED_PROTOCOLS_ALL) == 0) /* If no protocol is enabled, enable all */ + *flags |= SD_RESOLVED_PROTOCOLS_ALL; return 0; } @@ -281,6 +239,8 @@ static int bus_method_resolve_hostname(sd_bus_message *message, void *userdata, assert(message); assert(m); + assert_cc(sizeof(int) == sizeof(int32_t)); + r = sd_bus_message_read(message, "isit", &ifindex, &hostname, &family, &flags); if (r < 0) return r; @@ -288,41 +248,19 @@ static int bus_method_resolve_hostname(sd_bus_message *message, void *userdata, if (!IN_SET(family, AF_INET, AF_INET6, AF_UNSPEC)) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown address family %i", family); - r = dns_name_normalize(hostname, NULL); + r = dns_name_is_valid(hostname); if (r < 0) + return r; + if (r == 0) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid hostname '%s'", hostname); - r = check_ifindex_flags(ifindex, &flags, error); + r = check_ifindex_flags(ifindex, &flags, SD_RESOLVED_NO_SEARCH, error); if (r < 0) return r; - question = dns_question_new(family == AF_UNSPEC ? 2 : 1); - if (!question) - return -ENOMEM; - - if (family != AF_INET6) { - _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL; - - key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, hostname); - if (!key) - return -ENOMEM; - - r = dns_question_add(question, key); - if (r < 0) - return r; - } - - if (family != AF_INET) { - _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL; - - key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_AAAA, hostname); - if (!key) - return -ENOMEM; - - r = dns_question_add(question, key); - if (r < 0) - return r; - } + r = dns_question_new_address(&question, family, hostname); + if (r < 0) + return r; r = dns_query_new(m, &q, question, ifindex, flags); if (r < 0) @@ -330,27 +268,28 @@ static int bus_method_resolve_hostname(sd_bus_message *message, void *userdata, q->request = sd_bus_message_ref(message); q->request_family = family; - q->request_hostname = hostname; q->complete = bus_method_resolve_hostname_complete; r = dns_query_bus_track(q, message); if (r < 0) - return r; + goto fail; r = dns_query_go(q); - if (r < 0) { - dns_query_free(q); - return r; - } + if (r < 0) + goto fail; return 1; + +fail: + dns_query_free(q); + return r; } static void bus_method_resolve_address_complete(DnsQuery *q) { _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; - _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL; - unsigned added = 0, i; - int r; + DnsResourceRecord *rr; + unsigned added = 0; + int ifindex, r; assert(q); @@ -359,6 +298,16 @@ static void bus_method_resolve_address_complete(DnsQuery *q) { goto finish; } + r = dns_query_process_cname(q); + if (r == -ELOOP) { + r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_CNAME_LOOP, "CNAME loop detected, or CNAME resolving disabled on '%s'", dns_question_first_name(q->question)); + goto finish; + } + if (r < 0) + goto finish; + if (r > 0) /* This was a cname, and the query was restarted. */ + return; + r = sd_bus_message_new_method_return(q->request, &reply); if (r < 0) goto finish; @@ -368,16 +317,14 @@ static void bus_method_resolve_address_complete(DnsQuery *q) { goto finish; if (q->answer) { - answer = dns_answer_ref(q->answer); - - for (i = 0; i < answer->n_rrs; i++) { - r = dns_question_matches_rr(q->question, answer->items[i].rr); + DNS_ANSWER_FOREACH_IFINDEX(rr, ifindex, q->answer) { + r = dns_question_matches_rr(q->question, rr, NULL); if (r < 0) goto finish; if (r == 0) continue; - r = sd_bus_message_append(reply, "(is)", answer->items[i].ifindex, answer->items[i].rr->ptr.name); + r = sd_bus_message_append(reply, "(is)", ifindex, rr->ptr.name); if (r < 0) goto finish; @@ -385,12 +332,11 @@ static void bus_method_resolve_address_complete(DnsQuery *q) { } } - if (added == 0) { + if (added <= 0) { _cleanup_free_ char *ip = NULL; in_addr_to_string(q->request_family, &q->request_address, &ip); - - r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "Address '%s' does not have any RR of requested type", ip); + r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "Address '%s' does not have any RR of requested type", strna(ip)); goto finish; } @@ -407,16 +353,14 @@ static void bus_method_resolve_address_complete(DnsQuery *q) { finish: if (r < 0) { log_error_errno(r, "Failed to send address reply: %m"); - sd_bus_reply_method_errno(q->request, -r, NULL); + sd_bus_reply_method_errno(q->request, r, NULL); } dns_query_free(q); } static int bus_method_resolve_address(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL; _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL; - _cleanup_free_ char *reverse = NULL; Manager *m = userdata; int family, ifindex; uint64_t flags; @@ -428,6 +372,8 @@ static int bus_method_resolve_address(sd_bus_message *message, void *userdata, s assert(message); assert(m); + assert_cc(sizeof(int) == sizeof(int32_t)); + r = sd_bus_message_read(message, "ii", &ifindex, &family); if (r < 0) return r; @@ -446,54 +392,77 @@ static int bus_method_resolve_address(sd_bus_message *message, void *userdata, s if (r < 0) return r; - r = check_ifindex_flags(ifindex, &flags, error); + r = check_ifindex_flags(ifindex, &flags, 0, error); if (r < 0) return r; - r = dns_name_reverse(family, d, &reverse); + r = dns_question_new_reverse(&question, family, d); if (r < 0) return r; - question = dns_question_new(1); - if (!question) - return -ENOMEM; + r = dns_query_new(m, &q, question, ifindex, flags|SD_RESOLVED_NO_SEARCH); + if (r < 0) + return r; - key = dns_resource_key_new_consume(DNS_CLASS_IN, DNS_TYPE_PTR, reverse); - if (!key) - return -ENOMEM; + q->request = sd_bus_message_ref(message); + q->request_family = family; + memcpy(&q->request_address, d, sz); + q->complete = bus_method_resolve_address_complete; + + r = dns_query_bus_track(q, message); + if (r < 0) + goto fail; - reverse = NULL; + r = dns_query_go(q); + if (r < 0) + goto fail; - r = dns_question_add(question, key); + return 1; + +fail: + dns_query_free(q); + return r; +} + +static int bus_message_append_rr(sd_bus_message *m, DnsResourceRecord *rr, int ifindex) { + _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; + size_t start; + int r; + + assert(m); + assert(rr); + + r = sd_bus_message_open_container(m, 'r', "iqqay"); if (r < 0) return r; - r = dns_query_new(m, &q, question, ifindex, flags); + r = sd_bus_message_append(m, "iqq", + ifindex, + rr->key->class, + rr->key->type); if (r < 0) return r; - q->request = sd_bus_message_ref(message); - q->request_family = family; - memcpy(&q->request_address, d, sz); - q->complete = bus_method_resolve_address_complete; + r = dns_packet_new(&p, DNS_PROTOCOL_DNS, 0); + if (r < 0) + return r; - r = dns_query_bus_track(q, message); + p->refuse_compression = true; + + r = dns_packet_append_rr(p, rr, &start); if (r < 0) return r; - r = dns_query_go(q); - if (r < 0) { - dns_query_free(q); + r = sd_bus_message_append_array(m, 'y', DNS_PACKET_DATA(p) + start, p->size - start); + if (r < 0) return r; - } - return 1; + return sd_bus_message_close_container(m); } static void bus_method_resolve_record_complete(DnsQuery *q) { _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; - _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL; - unsigned added = 0, i; + unsigned added = 0; int r; assert(q); @@ -503,6 +472,16 @@ static void bus_method_resolve_record_complete(DnsQuery *q) { goto finish; } + r = dns_query_process_cname(q); + if (r == -ELOOP) { + r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_CNAME_LOOP, "CNAME loop detected, or CNAME resolving disabled on '%s'", dns_question_first_name(q->question)); + goto finish; + } + if (r < 0) + goto finish; + if (r > 0) /* Following a CNAME */ + return; + r = sd_bus_message_new_method_return(q->request, &reply); if (r < 0) goto finish; @@ -512,44 +491,17 @@ static void bus_method_resolve_record_complete(DnsQuery *q) { goto finish; if (q->answer) { - answer = dns_answer_ref(q->answer); - - for (i = 0; i < answer->n_rrs; i++) { - _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; - size_t start; + DnsResourceRecord *rr; + int ifindex; - r = dns_question_matches_rr(q->question, answer->items[i].rr); + DNS_ANSWER_FOREACH_IFINDEX(rr, ifindex, q->answer) { + r = dns_question_matches_rr(q->question, rr, NULL); if (r < 0) goto finish; if (r == 0) continue; - r = dns_packet_new(&p, DNS_PROTOCOL_DNS, 0); - if (r < 0) - goto finish; - - p->refuse_compression = true; - - r = dns_packet_append_rr(p, answer->items[i].rr, &start); - if (r < 0) - goto finish; - - r = sd_bus_message_open_container(reply, 'r', "iqqay"); - if (r < 0) - goto finish; - - r = sd_bus_message_append(reply, "iqq", - answer->items[i].ifindex, - answer->items[i].rr->key->class, - answer->items[i].rr->key->type); - if (r < 0) - goto finish; - - r = sd_bus_message_append_array(reply, 'y', DNS_PACKET_DATA(p) + start, p->size - start); - if (r < 0) - goto finish; - - r = sd_bus_message_close_container(reply); + r = bus_message_append_rr(reply, rr, ifindex); if (r < 0) goto finish; @@ -558,7 +510,7 @@ static void bus_method_resolve_record_complete(DnsQuery *q) { } if (added <= 0) { - r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "Name '%s' does not have any RR of the requested type", q->request_hostname); + r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "Name '%s' does not have any RR of the requested type", dns_question_first_name(q->question)); goto finish; } @@ -575,7 +527,7 @@ static void bus_method_resolve_record_complete(DnsQuery *q) { finish: if (r < 0) { log_error_errno(r, "Failed to send record reply: %m"); - sd_bus_reply_method_errno(q->request, -r, NULL); + sd_bus_reply_method_errno(q->request, r, NULL); } dns_query_free(q); @@ -594,15 +546,19 @@ static int bus_method_resolve_record(sd_bus_message *message, void *userdata, sd assert(message); assert(m); + assert_cc(sizeof(int) == sizeof(int32_t)); + r = sd_bus_message_read(message, "isqqt", &ifindex, &name, &class, &type, &flags); if (r < 0) return r; - r = dns_name_normalize(name, NULL); + r = dns_name_is_valid(name); if (r < 0) + return r; + if (r == 0) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid name '%s'", name); - r = check_ifindex_flags(ifindex, &flags, error); + r = check_ifindex_flags(ifindex, &flags, 0, error); if (r < 0) return r; @@ -618,32 +574,657 @@ static int bus_method_resolve_record(sd_bus_message *message, void *userdata, sd if (r < 0) return r; - r = dns_query_new(m, &q, question, ifindex, flags); + r = dns_query_new(m, &q, question, ifindex, flags|SD_RESOLVED_NO_SEARCH); if (r < 0) return r; q->request = sd_bus_message_ref(message); - q->request_hostname = name; q->complete = bus_method_resolve_record_complete; r = dns_query_bus_track(q, message); if (r < 0) - return r; + goto fail; r = dns_query_go(q); + if (r < 0) + goto fail; + + return 1; + +fail: + dns_query_free(q); + return r; +} + +static int append_srv(DnsQuery *q, sd_bus_message *reply, DnsResourceRecord *rr) { + _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *canonical = NULL; + DnsQuery *aux; + int r; + + assert(q); + assert(reply); + assert(rr); + assert(rr->key); + + if (rr->key->type != DNS_TYPE_SRV) + return 0; + + if ((q->flags & SD_RESOLVED_NO_ADDRESS) == 0) { + /* First, let's see if we could find an appropriate A or AAAA + * record for the SRV record */ + LIST_FOREACH(auxiliary_queries, aux, q->auxiliary_queries) { + DnsResourceRecord *zz; + + if (aux->state != DNS_TRANSACTION_SUCCESS) + continue; + if (aux->auxiliary_result != 0) + continue; + + r = dns_name_equal(dns_question_first_name(aux->question), rr->srv.name); + if (r < 0) + return r; + if (r == 0) + continue; + + DNS_ANSWER_FOREACH(zz, aux->answer) { + + r = dns_question_matches_rr(aux->question, zz, NULL); + if (r < 0) + return r; + if (r == 0) + continue; + + canonical = dns_resource_record_ref(zz); + break; + } + + if (canonical) + break; + } + + /* Is there are successful A/AAAA lookup for this SRV RR? If not, don't add it */ + if (!canonical) + return 0; + } + + r = sd_bus_message_open_container(reply, 'r', "qqqsa(iiay)s"); + if (r < 0) + return r; + + r = sd_bus_message_append( + reply, + "qqqs", + rr->srv.priority, rr->srv.weight, rr->srv.port, rr->srv.name); + if (r < 0) + return r; + + r = sd_bus_message_open_container(reply, 'a', "(iiay)"); + if (r < 0) + return r; + + if ((q->flags & SD_RESOLVED_NO_ADDRESS) == 0) { + LIST_FOREACH(auxiliary_queries, aux, q->auxiliary_queries) { + DnsResourceRecord *zz; + int ifindex; + + if (aux->state != DNS_TRANSACTION_SUCCESS) + continue; + if (aux->auxiliary_result != 0) + continue; + + r = dns_name_equal(dns_question_first_name(aux->question), rr->srv.name); + if (r < 0) + return r; + if (r == 0) + continue; + + DNS_ANSWER_FOREACH_IFINDEX(zz, ifindex, aux->answer) { + + r = dns_question_matches_rr(aux->question, zz, NULL); + if (r < 0) + return r; + if (r == 0) + continue; + + r = append_address(reply, zz, ifindex); + if (r < 0) + return r; + } + } + } + + r = sd_bus_message_close_container(reply); + if (r < 0) + return r; + + /* Note that above we appended the hostname as encoded in the + * SRV, and here the canonical hostname this maps to. */ + r = sd_bus_message_append(reply, "s", canonical ? DNS_RESOURCE_KEY_NAME(canonical->key) : rr->srv.name); + if (r < 0) + return r; + + r = sd_bus_message_close_container(reply); + if (r < 0) + return r; + + return 1; +} + +static int append_txt(sd_bus_message *reply, DnsResourceRecord *rr) { + DnsTxtItem *i; + int r; + + assert(reply); + assert(rr); + assert(rr->key); + + if (rr->key->type != DNS_TYPE_TXT) + return 0; + + LIST_FOREACH(items, i, rr->txt.items) { + + if (i->length <= 0) + continue; + + r = sd_bus_message_append_array(reply, 'y', i->data, i->length); + if (r < 0) + return r; + } + + return 1; +} + +static void resolve_service_all_complete(DnsQuery *q) { + _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *canonical = NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + _cleanup_free_ char *name = NULL, *type = NULL, *domain = NULL; + DnsQuery *aux; + unsigned added = false; + int r; + + assert(q); + + if (q->block_all_complete > 0) + return; + + if ((q->flags & SD_RESOLVED_NO_ADDRESS) == 0) { + DnsQuery *bad = NULL; + bool have_success = false; + + LIST_FOREACH(auxiliary_queries, aux, q->auxiliary_queries) { + + switch (aux->state) { + + case DNS_TRANSACTION_PENDING: + /* If an auxiliary query is still pending, let's wait */ + return; + + case DNS_TRANSACTION_SUCCESS: + if (aux->auxiliary_result == 0) + have_success = true; + else + bad = aux; + break; + + default: + bad = aux; + break; + } + } + + if (!have_success) { + /* We can only return one error, hence pick the last error we encountered */ + + assert(bad); + + if (bad->state == DNS_TRANSACTION_SUCCESS) { + assert(bad->auxiliary_result != 0); + + if (bad->auxiliary_result == -ELOOP) { + r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_CNAME_LOOP, "CNAME loop detected, or CNAME resolving disabled on '%s'", dns_question_first_name(bad->question)); + goto finish; + } + + r = bad->auxiliary_result; + goto finish; + } + + r = reply_query_state(bad); + goto finish; + } + } + + r = sd_bus_message_new_method_return(q->request, &reply); + if (r < 0) + goto finish; + + r = sd_bus_message_open_container(reply, 'a', "(qqqsa(iiay)s)"); + if (r < 0) + goto finish; + + if (q->answer) { + DnsResourceRecord *rr; + + DNS_ANSWER_FOREACH(rr, q->answer) { + r = dns_question_matches_rr(q->question, rr, NULL); + if (r < 0) + goto finish; + if (r == 0) + continue; + + r = append_srv(q, reply, rr); + if (r < 0) + goto finish; + if (r == 0) /* not an SRV record */ + continue; + + if (!canonical) + canonical = dns_resource_record_ref(rr); + + added++; + } + } + + if (added <= 0) { + r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "'%s' does not have any RR of the requested type", dns_question_first_name(q->question)); + goto finish; + } + + r = sd_bus_message_close_container(reply); + if (r < 0) + goto finish; + + r = sd_bus_message_open_container(reply, 'a', "ay"); + if (r < 0) + goto finish; + + if (q->answer) { + DnsResourceRecord *rr; + + DNS_ANSWER_FOREACH(rr, q->answer) { + r = dns_question_matches_rr(q->question, rr, NULL); + if (r < 0) + goto finish; + if (r == 0) + continue; + + r = append_txt(reply, rr); + if (r < 0) + goto finish; + } + } + + r = sd_bus_message_close_container(reply); + if (r < 0) + goto finish; + + assert(canonical); + r = dns_service_split(DNS_RESOURCE_KEY_NAME(canonical->key), &name, &type, &domain); + if (r < 0) + goto finish; + + r = sd_bus_message_append( + reply, + "ssst", + name, type, domain, + SD_RESOLVED_FLAGS_MAKE(q->answer_protocol, q->answer_family)); + if (r < 0) + goto finish; + + r = sd_bus_send(q->manager->bus, reply, NULL); + +finish: if (r < 0) { - dns_query_free(q); + log_error_errno(r, "Failed to send service reply: %m"); + sd_bus_reply_method_errno(q->request, r, NULL); + } + + dns_query_free(q); +} + +static void resolve_service_hostname_complete(DnsQuery *q) { + int r; + + assert(q); + assert(q->auxiliary_for); + + if (q->state != DNS_TRANSACTION_SUCCESS) { + resolve_service_all_complete(q->auxiliary_for); + return; + } + + r = dns_query_process_cname(q); + if (r > 0) /* This was a cname, and the query was restarted. */ + return; + + /* This auxiliary lookup is finished or failed, let's see if all are finished now. */ + q->auxiliary_result = r; + resolve_service_all_complete(q->auxiliary_for); +} + +static int resolve_service_hostname(DnsQuery *q, DnsResourceRecord *rr, int ifindex) { + _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL; + DnsQuery *aux; + int r; + + assert(q); + assert(rr); + assert(rr->key); + assert(rr->key->type == DNS_TYPE_SRV); + + /* OK, we found an SRV record for the service. Let's resolve + * the hostname included in it */ + + r = dns_question_new_address(&question, q->request_family, rr->srv.name); + if (r < 0) + return r; + + r = dns_query_new(q->manager, &aux, question, ifindex, q->flags|SD_RESOLVED_NO_SEARCH); + if (r < 0) return r; + + aux->request_family = q->request_family; + aux->complete = resolve_service_hostname_complete; + + r = dns_query_make_auxiliary(aux, q); + if (r == -EAGAIN) { + /* Too many auxiliary lookups? If so, don't complain, + * let's just not add this one, we already have more + * than enough */ + + dns_query_free(aux); + return 0; } + if (r < 0) + goto fail; + + /* Note that auxiliary queries do not track the original bus + * client, only the primary request does that. */ + + r = dns_query_go(aux); + if (r < 0) + goto fail; return 1; + +fail: + dns_query_free(aux); + return r; +} + +static void bus_method_resolve_service_complete(DnsQuery *q) { + unsigned found = 0; + int r; + + assert(q); + + if (q->state != DNS_TRANSACTION_SUCCESS) { + r = reply_query_state(q); + goto finish; + } + + r = dns_query_process_cname(q); + if (r == -ELOOP) { + r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_CNAME_LOOP, "CNAME loop detected, or CNAME resolving disabled on '%s'", dns_question_first_name(q->question)); + goto finish; + } + if (r < 0) + goto finish; + if (r > 0) /* This was a cname, and the query was restarted. */ + return; + + if (q->answer) { + DnsResourceRecord *rr; + int ifindex; + + DNS_ANSWER_FOREACH_IFINDEX(rr, ifindex, q->answer) { + r = dns_question_matches_rr(q->question, rr, NULL); + if (r < 0) + goto finish; + if (r == 0) + continue; + + if (rr->key->type != DNS_TYPE_SRV) + continue; + + if ((q->flags & SD_RESOLVED_NO_ADDRESS) == 0) { + q->block_all_complete ++; + r = resolve_service_hostname(q, rr, ifindex); + q->block_all_complete --; + + if (r < 0) + goto finish; + } + + found++; + } + } + + if (found <= 0) { + r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "'%s' does not have any RR of the requested type", dns_question_first_name(q->question)); + goto finish; + } + + /* Maybe we are already finished? check now... */ + resolve_service_all_complete(q); + return; + +finish: + if (r < 0) { + log_error_errno(r, "Failed to send service reply: %m"); + sd_bus_reply_method_errno(q->request, r, NULL); + } + + dns_query_free(q); +} + +static int bus_method_resolve_service(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL; + const char *name, *type, *domain, *joined; + _cleanup_free_ char *n = NULL; + Manager *m = userdata; + int family, ifindex; + uint64_t flags; + DnsQuery *q; + int r; + + assert(message); + assert(m); + + assert_cc(sizeof(int) == sizeof(int32_t)); + + r = sd_bus_message_read(message, "isssit", &ifindex, &name, &type, &domain, &family, &flags); + if (r < 0) + return r; + + if (!IN_SET(family, AF_INET, AF_INET6, AF_UNSPEC)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown address family %i", family); + + if (isempty(name)) + name = NULL; + else { + if (!dns_service_name_is_valid(name)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid service name '%s'", name); + } + + if (isempty(type)) + type = NULL; + else if (!dns_srv_type_is_valid(type)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid SRV service type '%s'", type); + + r = dns_name_is_valid(domain); + if (r < 0) + return r; + if (r == 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid domain '%s'", domain); + + if (name && !type) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Service name cannot be specified without service type."); + + r = check_ifindex_flags(ifindex, &flags, SD_RESOLVED_NO_TXT|SD_RESOLVED_NO_ADDRESS, error); + if (r < 0) + return r; + + if (type) { + /* If the type is specified, we generate the full domain name to look up ourselves */ + r = dns_service_join(name, type, domain, &n); + if (r < 0) + return r; + + joined = n; + } else + /* If no type is specified, we assume the domain + * contains the full domain name to lookup already */ + joined = domain; + + r = dns_question_new_service(&question, joined, !(flags & SD_RESOLVED_NO_TXT)); + if (r < 0) + return r; + + r = dns_query_new(m, &q, question, ifindex, flags|SD_RESOLVED_NO_SEARCH); + if (r < 0) + return r; + + q->request = sd_bus_message_ref(message); + q->request_family = family; + q->complete = bus_method_resolve_service_complete; + + r = dns_query_bus_track(q, message); + if (r < 0) + goto fail; + + r = dns_query_go(q); + if (r < 0) + goto fail; + + return 1; + +fail: + dns_query_free(q); + return r; +} + +static int append_dns_server(sd_bus_message *reply, DnsServer *s) { + int r; + + assert(reply); + assert(s); + + r = sd_bus_message_open_container(reply, 'r', "iiay"); + if (r < 0) + return r; + + r = sd_bus_message_append(reply, "ii", s->link ? s->link->ifindex : 0, s->family); + if (r < 0) + return r; + + r = sd_bus_message_append_array(reply, 'y', &s->address, FAMILY_ADDRESS_SIZE(s->family)); + if (r < 0) + return r; + + return sd_bus_message_close_container(reply); +} + +static int bus_property_get_dns_servers( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Manager *m = userdata; + unsigned c = 0; + DnsServer *s; + Iterator i; + Link *l; + int r; + + assert(reply); + assert(m); + + r = sd_bus_message_open_container(reply, 'a', "(iiay)"); + if (r < 0) + return r; + + LIST_FOREACH(servers, s, m->dns_servers) { + r = append_dns_server(reply, s); + if (r < 0) + return r; + + c++; + } + + HASHMAP_FOREACH(l, m->links, i) { + LIST_FOREACH(servers, s, l->dns_servers) { + r = append_dns_server(reply, s); + if (r < 0) + return r; + c++; + } + } + + if (c == 0) { + LIST_FOREACH(servers, s, m->fallback_dns_servers) { + r = append_dns_server(reply, s); + if (r < 0) + return r; + } + } + + return sd_bus_message_close_container(reply); +} + +static int bus_property_get_search_domains( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Manager *m = userdata; + DnsSearchDomain *d; + Iterator i; + Link *l; + int r; + + assert(reply); + assert(m); + + r = sd_bus_message_open_container(reply, 'a', "(is)"); + if (r < 0) + return r; + + LIST_FOREACH(domains, d, m->search_domains) { + r = sd_bus_message_append(reply, "(is)", 0, d->name); + if (r < 0) + return r; + } + + HASHMAP_FOREACH(l, m->links, i) { + LIST_FOREACH(domains, d, l->search_domains) { + r = sd_bus_message_append(reply, "is", l->ifindex, d->name); + if (r < 0) + return r; + } + } + + return sd_bus_message_close_container(reply); } static const sd_bus_vtable resolve_vtable[] = { SD_BUS_VTABLE_START(0), + SD_BUS_PROPERTY("LLMNRHostname", "s", NULL, offsetof(Manager, llmnr_hostname), 0), + SD_BUS_PROPERTY("DNSServers", "a(iiay)", bus_property_get_dns_servers, 0, 0), + SD_BUS_PROPERTY("SearchDomains", "a(is)", bus_property_get_search_domains, 0, 0), + SD_BUS_METHOD("ResolveHostname", "isit", "a(iiay)st", bus_method_resolve_hostname, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("ResolveAddress", "iiayt", "a(is)t", bus_method_resolve_address, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("ResolveRecord", "isqqt", "a(iqqay)t", bus_method_resolve_record, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("ResolveService", "isssit", "a(qqqsa(iiay)s)aayssst", bus_method_resolve_service, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_VTABLE_END, }; diff --git a/src/resolve/resolved-conf.c b/src/resolve/resolved-conf.c index 9207719551..3fc7d9ae3d 100644 --- a/src/resolve/resolved-conf.c +++ b/src/resolve/resolved-conf.c @@ -27,53 +27,99 @@ #include "resolved-conf.h" #include "string-util.h" -int manager_parse_dns_server(Manager *m, DnsServerType type, const char *string) { - DnsServer *first; +int manager_add_dns_server_by_string(Manager *m, DnsServerType type, const char *word) { + union in_addr_union address; + int family, r; + DnsServer *s; + + assert(m); + assert(word); + + r = in_addr_from_string_auto(word, &family, &address); + if (r < 0) + return r; + + /* Filter out duplicates */ + s = dns_server_find(manager_get_first_dns_server(m, type), family, &address); + if (s) { + /* + * Drop the marker. This is used to find the servers + * that ceased to exist, see + * manager_mark_dns_servers() and + * manager_flush_marked_dns_servers(). + */ + dns_server_move_back_and_unmark(s); + return 0; + } + + return dns_server_new(m, NULL, type, NULL, family, &address); +} + +int manager_parse_dns_server_string_and_warn(Manager *m, DnsServerType type, const char *string) { int r; assert(m); assert(string); - first = type == DNS_SERVER_FALLBACK ? m->fallback_dns_servers : m->dns_servers; - for(;;) { _cleanup_free_ char *word = NULL; - union in_addr_union addr; - bool found = false; - DnsServer *s; - int family; r = extract_first_word(&string, &word, NULL, 0); if (r < 0) - return log_error_errno(r, "Failed to parse resolved dns server syntax \"%s\": %m", string); + return r; if (r == 0) break; - r = in_addr_from_string_auto(word, &family, &addr); - if (r < 0) { - log_warning("Ignoring invalid DNS address '%s'", word); - continue; - } + r = manager_add_dns_server_by_string(m, type, word); + if (r < 0) + log_warning_errno(r, "Failed to add DNS server address '%s', ignoring.", word); + } + + return 0; +} - /* Filter out duplicates */ - LIST_FOREACH(servers, s, first) - if (s->family == family && in_addr_equal(family, &s->address, &addr)) { - found = true; - break; - } +int manager_add_search_domain_by_string(Manager *m, const char *domain) { + DnsSearchDomain *d; + int r; - if (found) - continue; + assert(m); + assert(domain); - r = dns_server_new(m, NULL, type, NULL, family, &addr); + r = dns_search_domain_find(m->search_domains, domain, &d); + if (r < 0) + return r; + if (r > 0) { + dns_search_domain_move_back_and_unmark(d); + return 0; + } + + return dns_search_domain_new(m, NULL, DNS_SEARCH_DOMAIN_SYSTEM, NULL, domain); +} + +int manager_parse_search_domains_and_warn(Manager *m, const char *string) { + int r; + + assert(m); + assert(string); + + for(;;) { + _cleanup_free_ char *word = NULL; + + r = extract_first_word(&string, &word, NULL, EXTRACT_QUOTES); if (r < 0) return r; + if (r == 0) + break; + + r = manager_add_search_domain_by_string(m, word); + if (r < 0) + log_warning_errno(r, "Failed to add search domain '%s', ignoring.", word); } return 0; } -int config_parse_dnsv( +int config_parse_dns_servers( const char *unit, const char *filename, unsigned line, @@ -95,10 +141,10 @@ int config_parse_dnsv( if (isempty(rvalue)) /* Empty assignment means clear the list */ - manager_flush_dns_servers(m, ltype); + dns_server_unlink_all(manager_get_first_dns_server(m, ltype)); else { /* Otherwise, add to the list */ - r = manager_parse_dns_server(m, ltype, rvalue); + r = manager_parse_dns_server_string_and_warn(m, ltype, rvalue); if (r < 0) { log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse DNS server string '%s'. Ignoring.", rvalue); return 0; @@ -109,6 +155,47 @@ int config_parse_dnsv( * /etc/resolv.conf */ if (ltype == DNS_SERVER_SYSTEM) m->read_resolv_conf = false; + if (ltype == DNS_SERVER_FALLBACK) + m->need_builtin_fallbacks = false; + + return 0; +} + +int config_parse_search_domains( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + Manager *m = userdata; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(m); + + if (isempty(rvalue)) + /* Empty assignment means clear the list */ + dns_search_domain_unlink_all(m->search_domains); + else { + /* Otherwise, add to the list */ + r = manager_parse_search_domains_and_warn(m, rvalue); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse search domains string '%s'. Ignoring.", rvalue); + return 0; + } + } + + /* If we have a manual setting, then we stop reading + * /etc/resolv.conf */ + m->read_resolv_conf = false; return 0; } @@ -148,11 +235,24 @@ int config_parse_support( } int manager_parse_config_file(Manager *m) { + int r; + assert(m); - return config_parse_many(PKGSYSCONFDIR "/resolved.conf", - CONF_PATHS_NULSTR("systemd/resolved.conf.d"), - "Resolve\0", - config_item_perf_lookup, resolved_gperf_lookup, - false, m); + r = config_parse_many(PKGSYSCONFDIR "/resolved.conf", + CONF_PATHS_NULSTR("systemd/resolved.conf.d"), + "Resolve\0", + config_item_perf_lookup, resolved_gperf_lookup, + false, m); + if (r < 0) + return r; + + if (m->need_builtin_fallbacks) { + r = manager_parse_dns_server_string_and_warn(m, DNS_SERVER_FALLBACK, DNS_SERVERS); + if (r < 0) + return r; + } + + return 0; + } diff --git a/src/resolve/resolved-conf.h b/src/resolve/resolved-conf.h index b3dbea7b6b..28d2549d35 100644 --- a/src/resolve/resolved-conf.h +++ b/src/resolve/resolved-conf.h @@ -23,10 +23,16 @@ #include "resolved-manager.h" -int manager_parse_dns_server(Manager *m, DnsServerType type, const char *string); int manager_parse_config_file(Manager *m); +int manager_add_search_domain_by_string(Manager *m, const char *domain); +int manager_parse_search_domains_and_warn(Manager *m, const char *string); + +int manager_add_dns_server_by_string(Manager *m, DnsServerType type, const char *word); +int manager_parse_dns_server_string_and_warn(Manager *m, DnsServerType type, const char *string); + const struct ConfigPerfItem* resolved_gperf_lookup(const char *key, unsigned length); -int config_parse_dnsv(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_dns_servers(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_search_domains(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_support(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); diff --git a/src/resolve/resolved-def.h b/src/resolve/resolved-def.h index 086d111205..be29f51663 100644 --- a/src/resolve/resolved-def.h +++ b/src/resolve/resolved-def.h @@ -21,10 +21,13 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#define SD_RESOLVED_DNS ((uint64_t) 1) -#define SD_RESOLVED_LLMNR_IPV4 ((uint64_t) 2) -#define SD_RESOLVED_LLMNR_IPV6 ((uint64_t) 4) -#define SD_RESOLVED_LLMNR (SD_RESOLVED_LLMNR_IPV4|SD_RESOLVED_LLMNR_IPV6) +#define SD_RESOLVED_DNS (UINT64_C(1) << 0) +#define SD_RESOLVED_LLMNR_IPV4 (UINT64_C(1) << 1) +#define SD_RESOLVED_LLMNR_IPV6 (UINT64_C(1) << 2) +#define SD_RESOLVED_NO_CNAME (UINT64_C(1) << 5) +#define SD_RESOLVED_NO_TXT (UINT64_C(1) << 6) +#define SD_RESOLVED_NO_ADDRESS (UINT64_C(1) << 7) +#define SD_RESOLVED_NO_SEARCH (UINT64_C(1) << 8) -#define SD_RESOLVED_FLAGS_ALL (SD_RESOLVED_DNS|SD_RESOLVED_LLMNR_IPV4|SD_RESOLVED_LLMNR_IPV6) -#define SD_RESOLVED_FLAGS_DEFAULT SD_RESOLVED_FLAGS_ALL +#define SD_RESOLVED_LLMNR (SD_RESOLVED_LLMNR_IPV4|SD_RESOLVED_LLMNR_IPV6) +#define SD_RESOLVED_PROTOCOLS_ALL (SD_RESOLVED_LLMNR|SD_RESOLVED_DNS) diff --git a/src/resolve/resolved-dns-answer.c b/src/resolve/resolved-dns-answer.c index 3cf9c68074..4db67f7278 100644 --- a/src/resolve/resolved-dns-answer.c +++ b/src/resolve/resolved-dns-answer.c @@ -141,7 +141,7 @@ int dns_answer_contains(DnsAnswer *a, DnsResourceKey *key) { return 0; for (i = 0; i < a->n_rrs; i++) { - r = dns_resource_key_match_rr(key, a->items[i].rr); + r = dns_resource_key_match_rr(key, a->items[i].rr, NULL); if (r < 0) return r; if (r > 0) diff --git a/src/resolve/resolved-dns-answer.h b/src/resolve/resolved-dns-answer.h index 044d73b19c..8814919deb 100644 --- a/src/resolve/resolved-dns-answer.h +++ b/src/resolve/resolved-dns-answer.h @@ -58,3 +58,20 @@ void dns_answer_order_by_scope(DnsAnswer *a, bool prefer_link_local); int dns_answer_reserve(DnsAnswer **a, unsigned n_free); DEFINE_TRIVIAL_CLEANUP_FUNC(DnsAnswer*, dns_answer_unref); + +#define DNS_ANSWER_FOREACH(kk, a) \ + for (unsigned _i = ({ \ + (kk) = ((a) && (a)->n_rrs > 0) ? (a)->items[0].rr : NULL; \ + 0; \ + }); \ + (a) && ((_i) < (a)->n_rrs); \ + _i++, (kk) = (_i < (a)->n_rrs ? (a)->items[_i].rr : NULL)) + +#define DNS_ANSWER_FOREACH_IFINDEX(kk, ifindex, a) \ + for (unsigned _i = ({ \ + (kk) = ((a) && (a)->n_rrs > 0) ? (a)->items[0].rr : NULL; \ + (ifindex) = ((a) && (a)->n_rrs > 0) ? (a)->items[0].ifindex : 0; \ + 0; \ + }); \ + (a) && ((_i) < (a)->n_rrs); \ + _i++, (kk) = ((_i < (a)->n_rrs) ? (a)->items[_i].rr : NULL), (ifindex) = ((_i < (a)->n_rrs) ? (a)->items[_i].ifindex : 0)) diff --git a/src/resolve/resolved-dns-cache.c b/src/resolve/resolved-dns-cache.c index 04f64022e0..d963ce6e00 100644 --- a/src/resolve/resolved-dns-cache.c +++ b/src/resolve/resolved-dns-cache.c @@ -20,8 +20,10 @@ ***/ #include "alloc-util.h" +#include "dns-domain.h" #include "resolved-dns-cache.h" #include "resolved-dns-packet.h" +#include "string-util.h" /* Never cache more than 1K entries */ #define CACHE_MAX 1024 @@ -521,25 +523,53 @@ fail: static DnsCacheItem *dns_cache_get_by_key_follow_cname(DnsCache *c, DnsResourceKey *k) { _cleanup_(dns_resource_key_unrefp) DnsResourceKey *cname_key = NULL; - DnsCacheItem *i, *j; + DnsCacheItem *i; + const char *n; + int r; assert(c); assert(k); + /* If we hit some OOM error, or suchlike, we don't care too + * much, after all this is just a cache */ + i = hashmap_get(c->by_key, k); - if (i || k->type == DNS_TYPE_CNAME) + if (i || k->type == DNS_TYPE_CNAME || k->type == DNS_TYPE_DNAME) return i; - /* check if we have a CNAME record instead */ + /* Check if we have a CNAME record instead */ cname_key = dns_resource_key_new_cname(k); if (!cname_key) return NULL; - j = hashmap_get(c->by_key, cname_key); - if (j) - return j; + i = hashmap_get(c->by_key, cname_key); + if (i) + return i; + + /* OK, let's look for cached DNAME records. */ + n = DNS_RESOURCE_KEY_NAME(k); + for (;;) { + _cleanup_(dns_resource_key_unrefp) DnsResourceKey *dname_key = NULL; + char label[DNS_LABEL_MAX]; + + if (isempty(n)) + return NULL; - return i; + dname_key = dns_resource_key_new(k->class, DNS_TYPE_DNAME, n); + if (!dname_key) + return NULL; + + i = hashmap_get(c->by_key, dname_key); + if (i) + return i; + + /* Jump one label ahead */ + r = dns_label_unescape(&n, label, sizeof(label)); + if (r <= 0) + return NULL; + } + + return NULL; } int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **ret) { diff --git a/src/resolve/resolved-dns-cache.h b/src/resolve/resolved-dns-cache.h index 60cf6a4784..164435b4fb 100644 --- a/src/resolve/resolved-dns-cache.h +++ b/src/resolve/resolved-dns-cache.h @@ -23,18 +23,18 @@ #include "hashmap.h" +#include "list.h" #include "prioq.h" #include "time-util.h" -#include "list.h" typedef struct DnsCache { Hashmap *by_key; Prioq *by_expiry; } DnsCache; -#include "resolved-dns-rr.h" -#include "resolved-dns-question.h" #include "resolved-dns-answer.h" +#include "resolved-dns-question.h" +#include "resolved-dns-rr.h" void dns_cache_flush(DnsCache *c); void dns_cache_prune(DnsCache *c); diff --git a/src/resolve/resolved-dns-packet.c b/src/resolve/resolved-dns-packet.c index f23b3cf893..40b662246f 100644 --- a/src/resolve/resolved-dns-packet.c +++ b/src/resolve/resolved-dns-packet.c @@ -370,6 +370,28 @@ int dns_packet_append_string(DnsPacket *p, const char *s, size_t *start) { return 0; } +int dns_packet_append_raw_string(DnsPacket *p, const void *s, size_t size, size_t *start) { + void *d; + int r; + + assert(p); + assert(s || size == 0); + + if (size > 255) + return -E2BIG; + + r = dns_packet_extend(p, 1 + size, &d, start); + if (r < 0) + return r; + + ((uint8_t*) d)[0] = (uint8_t) size; + + if (size > 0) + memcpy(((uint8_t*) d) + 1, s, size); + + return 0; +} + int dns_packet_append_label(DnsPacket *p, const char *d, size_t l, size_t *start) { void *w; int r; @@ -643,19 +665,20 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *star break; case DNS_TYPE_SPF: /* exactly the same as TXT */ - case DNS_TYPE_TXT: { - char **s; + case DNS_TYPE_TXT: - if (strv_isempty(rr->txt.strings)) { + if (!rr->txt.items) { /* RFC 6763, section 6.1 suggests to generate * single empty string for an empty array. */ - r = dns_packet_append_string(p, "", NULL); + r = dns_packet_append_raw_string(p, NULL, 0, NULL); if (r < 0) goto fail; } else { - STRV_FOREACH(s, rr->txt.strings) { - r = dns_packet_append_string(p, *s, NULL); + DnsTxtItem *i; + + LIST_FOREACH(items, i, rr->txt.items) { + r = dns_packet_append_raw_string(p, i->data, i->length, NULL); if (r < 0) goto fail; } @@ -663,7 +686,6 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *star r = 0; break; - } case DNS_TYPE_A: r = dns_packet_append_blob(p, &rr->a.in_addr, sizeof(struct in_addr), NULL); @@ -1062,6 +1084,35 @@ fail: return r; } +int dns_packet_read_raw_string(DnsPacket *p, const void **ret, size_t *size, size_t *start) { + size_t saved_rindex; + uint8_t c; + int r; + + assert(p); + + saved_rindex = p->rindex; + + r = dns_packet_read_uint8(p, &c, NULL); + if (r < 0) + goto fail; + + r = dns_packet_read(p, c, ret, NULL); + if (r < 0) + goto fail; + + if (size) + *size = c; + if (start) + *start = saved_rindex; + + return 0; + +fail: + dns_packet_rewind(p, saved_rindex); + return r; +} + int dns_packet_read_name( DnsPacket *p, char **_ret, @@ -1094,7 +1145,6 @@ int dns_packet_read_name( /* End of name */ break; else if (c <= 63) { - _cleanup_free_ char *t = NULL; const char *label; /* Literal label */ @@ -1102,21 +1152,20 @@ int dns_packet_read_name( if (r < 0) goto fail; - r = dns_label_escape(label, c, &t); - if (r < 0) - goto fail; - - if (!GREEDY_REALLOC(ret, allocated, n + !first + strlen(t) + 1)) { + if (!GREEDY_REALLOC(ret, allocated, n + !first + DNS_LABEL_ESCAPED_MAX)) { r = -ENOMEM; goto fail; } - if (!first) - ret[n++] = '.'; - else + if (first) first = false; + else + ret[n++] = '.'; + + r = dns_label_escape(label, c, ret + n, DNS_LABEL_ESCAPED_MAX); + if (r < 0) + goto fail; - memcpy(ret + n, t, r); n += r; continue; } else if (allow_compression && (c & 0xc0) == 0xc0) { @@ -1412,24 +1461,37 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start) { case DNS_TYPE_SPF: /* exactly the same as TXT */ case DNS_TYPE_TXT: if (rdlength <= 0) { + DnsTxtItem *i; /* RFC 6763, section 6.1 suggests to treat * empty TXT RRs as equivalent to a TXT record * with a single empty string. */ - r = strv_extend(&rr->txt.strings, ""); - if (r < 0) - goto fail; + i = malloc0(offsetof(DnsTxtItem, data) + 1); /* for safety reasons we add an extra NUL byte */ + if (!i) + return -ENOMEM; + + rr->txt.items = i; } else { + DnsTxtItem *last = NULL; + while (p->rindex < offset + rdlength) { - char *s; + DnsTxtItem *i; + const void *data; + size_t sz; - r = dns_packet_read_string(p, &s, NULL); + r = dns_packet_read_raw_string(p, &data, &sz, NULL); if (r < 0) - goto fail; + return r; - r = strv_consume(&rr->txt.strings, s); - if (r < 0) - goto fail; + i = malloc0(offsetof(DnsTxtItem, data) + sz + 1); /* extra NUL byte at the end */ + if (!i) + return -ENOMEM; + + memcpy(i->data, data, sz); + i->length = sz; + + LIST_INSERT_AFTER(items, rr->txt.items, last, i); + last = i; } } @@ -1682,12 +1744,9 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start) { if (r < 0) goto fail; - /* The types bitmap must contain at least the NSEC record itself, so an empty bitmap means - something went wrong */ - if (bitmap_isclear(rr->nsec.types)) { - r = -EBADMSG; - goto fail; - } + /* We accept empty NSEC bitmaps. The bit indicating the presence of the NSEC record itself + * is redundant and in e.g., RFC4956 this fact is used to define a use for NSEC records + * without the NSEC bit set. */ break; diff --git a/src/resolve/resolved-dns-packet.h b/src/resolve/resolved-dns-packet.h index fbbabaf232..90b5a7c8bd 100644 --- a/src/resolve/resolved-dns-packet.h +++ b/src/resolve/resolved-dns-packet.h @@ -21,21 +21,21 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <netinet/udp.h> #include <netinet/ip.h> +#include <netinet/udp.h> -#include "macro.h" -#include "sparse-endian.h" #include "hashmap.h" #include "in-addr-util.h" +#include "macro.h" +#include "sparse-endian.h" typedef struct DnsPacketHeader DnsPacketHeader; typedef struct DnsPacket DnsPacket; -#include "resolved-dns-rr.h" -#include "resolved-dns-question.h" -#include "resolved-dns-answer.h" #include "resolved-def.h" +#include "resolved-dns-answer.h" +#include "resolved-dns-question.h" +#include "resolved-dns-rr.h" typedef enum DnsProtocol { DNS_PROTOCOL_DNS, @@ -155,9 +155,9 @@ int dns_packet_append_uint8(DnsPacket *p, uint8_t v, size_t *start); int dns_packet_append_uint16(DnsPacket *p, uint16_t v, size_t *start); int dns_packet_append_uint32(DnsPacket *p, uint32_t v, size_t *start); int dns_packet_append_string(DnsPacket *p, const char *s, size_t *start); +int dns_packet_append_raw_string(DnsPacket *p, const void *s, size_t size, size_t *start); int dns_packet_append_label(DnsPacket *p, const char *s, size_t l, size_t *start); -int dns_packet_append_name(DnsPacket *p, const char *name, - bool allow_compression, size_t *start); +int dns_packet_append_name(DnsPacket *p, const char *name, bool allow_compression, size_t *start); int dns_packet_append_key(DnsPacket *p, const DnsResourceKey *key, size_t *start); int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *start); @@ -167,8 +167,8 @@ int dns_packet_read_uint8(DnsPacket *p, uint8_t *ret, size_t *start); int dns_packet_read_uint16(DnsPacket *p, uint16_t *ret, size_t *start); int dns_packet_read_uint32(DnsPacket *p, uint32_t *ret, size_t *start); int dns_packet_read_string(DnsPacket *p, char **ret, size_t *start); -int dns_packet_read_name(DnsPacket *p, char **ret, - bool allow_compression, size_t *start); +int dns_packet_read_raw_string(DnsPacket *p, const void **ret, size_t *size, size_t *start); +int dns_packet_read_name(DnsPacket *p, char **ret, bool allow_compression, size_t *start); int dns_packet_read_key(DnsPacket *p, DnsResourceKey **ret, size_t *start); int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start); @@ -177,6 +177,14 @@ void dns_packet_rewind(DnsPacket *p, size_t idx); int dns_packet_skip_question(DnsPacket *p); int dns_packet_extract(DnsPacket *p); +static inline bool DNS_PACKET_SHALL_CACHE(DnsPacket *p) { + /* Never cache data originating from localhost, under the + * assumption, that it's coming from a locally DNS forwarder + * or server, that is caching on its own. */ + + return in_addr_is_localhost(p->family, &p->sender) == 0; +} + enum { DNS_RCODE_SUCCESS = 0, DNS_RCODE_FORMERR = 1, diff --git a/src/resolve/resolved-dns-query.c b/src/resolve/resolved-dns-query.c index f7cb84e2a6..a96cf439ad 100644 --- a/src/resolve/resolved-dns-query.c +++ b/src/resolve/resolved-dns-query.c @@ -30,29 +30,286 @@ #define CNAME_MAX 8 #define QUERIES_MAX 2048 +#define AUXILIARY_QUERIES_MAX 64 -static void dns_query_stop(DnsQuery *q) { - DnsTransaction *t; +static int dns_query_candidate_new(DnsQueryCandidate **ret, DnsQuery *q, DnsScope *s) { + DnsQueryCandidate *c; + assert(ret); assert(q); + assert(s); - q->timeout_event_source = sd_event_source_unref(q->timeout_event_source); + c = new0(DnsQueryCandidate, 1); + if (!c) + return -ENOMEM; + + c->query = q; + c->scope = s; + + LIST_PREPEND(candidates_by_query, q->candidates, c); + LIST_PREPEND(candidates_by_scope, s->query_candidates, c); + + *ret = c; + return 0; +} - while ((t = set_steal_first(q->transactions))) { - set_remove(t->queries, q); +static void dns_query_candidate_stop(DnsQueryCandidate *c) { + DnsTransaction *t; + + assert(c); + + while ((t = set_steal_first(c->transactions))) { + set_remove(t->query_candidates, c); dns_transaction_gc(t); } } +DnsQueryCandidate* dns_query_candidate_free(DnsQueryCandidate *c) { + + if (!c) + return NULL; + + dns_query_candidate_stop(c); + + set_free(c->transactions); + dns_search_domain_unref(c->search_domain); + + if (c->query) + LIST_REMOVE(candidates_by_query, c->query->candidates, c); + + if (c->scope) + LIST_REMOVE(candidates_by_scope, c->scope->query_candidates, c); + + free(c); + + return NULL; +} + +static int dns_query_candidate_next_search_domain(DnsQueryCandidate *c) { + _cleanup_(dns_search_domain_unrefp) DnsSearchDomain *previous = NULL; + DnsSearchDomain *next = NULL; + + assert(c); + + if (c->search_domain && c->search_domain->linked) { + next = c->search_domain->domains_next; + + if (!next) /* We hit the end of the list */ + return 0; + + } else { + next = dns_scope_get_search_domains(c->scope); + + if (!next) /* OK, there's nothing. */ + return 0; + } + + dns_search_domain_unref(c->search_domain); + c->search_domain = dns_search_domain_ref(next); + + return 1; +} + +static int dns_query_candidate_add_transaction(DnsQueryCandidate *c, DnsResourceKey *key) { + DnsTransaction *t; + int r; + + assert(c); + assert(key); + + r = set_ensure_allocated(&c->transactions, NULL); + if (r < 0) + return r; + + t = dns_scope_find_transaction(c->scope, key, true); + if (!t) { + r = dns_transaction_new(&t, c->scope, key); + if (r < 0) + return r; + } + + r = set_ensure_allocated(&t->query_candidates, NULL); + if (r < 0) + goto gc; + + r = set_put(t->query_candidates, c); + if (r < 0) + goto gc; + + r = set_put(c->transactions, t); + if (r < 0) { + set_remove(t->query_candidates, c); + goto gc; + } + + return 0; + +gc: + dns_transaction_gc(t); + return r; +} + +static int dns_query_candidate_go(DnsQueryCandidate *c) { + DnsTransaction *t; + Iterator i; + int r; + + assert(c); + + /* Start the transactions that are not started yet */ + SET_FOREACH(t, c->transactions, i) { + if (t->state != DNS_TRANSACTION_NULL) + continue; + + r = dns_transaction_go(t); + if (r < 0) + return r; + } + + return 0; +} + +static DnsTransactionState dns_query_candidate_state(DnsQueryCandidate *c) { + DnsTransactionState state = DNS_TRANSACTION_NO_SERVERS; + DnsTransaction *t; + Iterator i; + + assert(c); + + if (c->error_code != 0) + return DNS_TRANSACTION_RESOURCES; + + SET_FOREACH(t, c->transactions, i) { + + switch (t->state) { + + case DNS_TRANSACTION_PENDING: + case DNS_TRANSACTION_NULL: + return t->state; + + case DNS_TRANSACTION_SUCCESS: + state = t->state; + break; + + default: + if (state != DNS_TRANSACTION_SUCCESS) + state = t->state; + + break; + } + } + + return state; +} + +static int dns_query_candidate_setup_transactions(DnsQueryCandidate *c) { + DnsResourceKey *key; + int n = 0, r; + + assert(c); + + dns_query_candidate_stop(c); + + /* Create one transaction per question key */ + DNS_QUESTION_FOREACH(key, c->query->question) { + _cleanup_(dns_resource_key_unrefp) DnsResourceKey *new_key = NULL; + + if (c->search_domain) { + r = dns_resource_key_new_append_suffix(&new_key, key, c->search_domain->name); + if (r < 0) + goto fail; + } + + r = dns_query_candidate_add_transaction(c, new_key ?: key); + if (r < 0) + goto fail; + + n++; + } + + return n; + +fail: + dns_query_candidate_stop(c); + return r; +} + +void dns_query_candidate_ready(DnsQueryCandidate *c) { + DnsTransactionState state; + int r; + + assert(c); + + state = dns_query_candidate_state(c); + + if (IN_SET(state, DNS_TRANSACTION_PENDING, DNS_TRANSACTION_NULL)) + return; + + if (state != DNS_TRANSACTION_SUCCESS && c->search_domain) { + + r = dns_query_candidate_next_search_domain(c); + if (r < 0) + goto fail; + + if (r > 0) { + /* OK, there's another search domain to try, let's do so. */ + + r = dns_query_candidate_setup_transactions(c); + if (r < 0) + goto fail; + + if (r > 0) { + /* New transactions where queued. Start them and wait */ + + r = dns_query_candidate_go(c); + if (r < 0) + goto fail; + + return; + } + } + + } + + dns_query_ready(c->query); + return; + +fail: + log_warning_errno(r, "Failed to follow search domains: %m"); + c->error_code = r; + dns_query_ready(c->query); +} + +static void dns_query_stop(DnsQuery *q) { + DnsQueryCandidate *c; + + assert(q); + + q->timeout_event_source = sd_event_source_unref(q->timeout_event_source); + + LIST_FOREACH(candidates_by_query, c, q->candidates) + dns_query_candidate_stop(c); +} + DnsQuery *dns_query_free(DnsQuery *q) { if (!q) return NULL; - dns_query_stop(q); - set_free(q->transactions); + while (q->auxiliary_queries) + dns_query_free(q->auxiliary_queries); + + if (q->auxiliary_for) { + assert(q->auxiliary_for->n_auxiliary_queries > 0); + q->auxiliary_for->n_auxiliary_queries--; + LIST_REMOVE(auxiliary_queries, q->auxiliary_for->auxiliary_queries, q); + } + + while (q->candidates) + dns_query_candidate_free(q->candidates); dns_question_unref(q->question); dns_answer_unref(q->answer); + dns_search_domain_unref(q->answer_search_domain); sd_bus_message_unref(q->request); sd_bus_track_unref(q->bus_track); @@ -75,7 +332,7 @@ int dns_query_new(Manager *m, DnsQuery **ret, DnsQuestion *question, int ifindex assert(m); assert(question); - r = dns_question_is_valid(question); + r = dns_question_is_valid_for_query(question); if (r < 0) return r; @@ -89,6 +346,8 @@ int dns_query_new(Manager *m, DnsQuery **ret, DnsQuestion *question, int ifindex q->question = dns_question_ref(question); q->ifindex = ifindex; q->flags = flags; + q->answer_family = AF_UNSPEC; + q->answer_protocol = _DNS_PROTOCOL_INVALID; for (i = 0; i < question->n_keys; i++) { _cleanup_free_ char *p; @@ -111,6 +370,29 @@ int dns_query_new(Manager *m, DnsQuery **ret, DnsQuestion *question, int ifindex return 0; } +int dns_query_make_auxiliary(DnsQuery *q, DnsQuery *auxiliary_for) { + assert(q); + assert(auxiliary_for); + + /* Ensure that that the query is not auxiliary yet, and + * nothing else is auxiliary to it either */ + assert(!q->auxiliary_for); + assert(!q->auxiliary_queries); + + /* Ensure that the unit we shall be made auxiliary for isn't + * auxiliary itself */ + assert(!auxiliary_for->auxiliary_for); + + if (auxiliary_for->n_auxiliary_queries >= AUXILIARY_QUERIES_MAX) + return -EAGAIN; + + LIST_PREPEND(auxiliary_queries, auxiliary_for->auxiliary_queries, q); + q->auxiliary_for = auxiliary_for; + + auxiliary_for->n_auxiliary_queries++; + return 0; +} + static void dns_query_complete(DnsQuery *q, DnsTransactionState state) { assert(q); assert(!IN_SET(state, DNS_TRANSACTION_NULL, DNS_TRANSACTION_PENDING)); @@ -137,64 +419,40 @@ static int on_query_timeout(sd_event_source *s, usec_t usec, void *userdata) { return 0; } -static int dns_query_add_transaction(DnsQuery *q, DnsScope *s, DnsResourceKey *key) { - DnsTransaction *t; +static int dns_query_add_candidate(DnsQuery *q, DnsScope *s) { + DnsQueryCandidate *c; int r; assert(q); assert(s); - assert(key); - r = set_ensure_allocated(&q->transactions, NULL); + r = dns_query_candidate_new(&c, q, s); if (r < 0) return r; - t = dns_scope_find_transaction(s, key, true); - if (!t) { - r = dns_transaction_new(&t, s, key); - if (r < 0) - return r; - } - - r = set_ensure_allocated(&t->queries, NULL); + /* If this a single-label domain on DNS, we might append a suitable search domain first. */ + r = dns_scope_name_needs_search_domain(s, dns_question_first_name(q->question)); if (r < 0) - goto gc; - - r = set_put(t->queries, q); - if (r < 0) - goto gc; + goto fail; + if (r > 0) { + /* OK, we need a search domain now. Let's find one for this scope */ - r = set_put(q->transactions, t); - if (r < 0) { - set_remove(t->queries, q); - goto gc; + r = dns_query_candidate_next_search_domain(c); + if (r <= 0) /* if there's no search domain, then we won't add any transaction. */ + goto fail; } + r = dns_query_candidate_setup_transactions(c); + if (r < 0) + goto fail; + return 0; -gc: - dns_transaction_gc(t); +fail: + dns_query_candidate_free(c); return r; } -static int dns_query_add_transaction_split(DnsQuery *q, DnsScope *s) { - unsigned i; - int r; - - assert(q); - assert(s); - - /* Create one transaction per question key */ - - for (i = 0; i < q->question->n_keys; i++) { - r = dns_query_add_transaction(q, s, q->question->keys[i]); - if (r < 0) - return r; - } - - return 0; -} - static int SYNTHESIZE_IFINDEX(int ifindex) { /* When the caller asked for resolving on a specific @@ -597,9 +855,9 @@ static int dns_query_synthesize_reply(DnsQuery *q, DnsTransactionState *state) { q->answer = answer; answer = NULL; - q->answer_family = SYNTHESIZE_FAMILY(q->flags); - q->answer_protocol = SYNTHESIZE_PROTOCOL(q->flags); q->answer_rcode = DNS_RCODE_SUCCESS; + q->answer_protocol = SYNTHESIZE_PROTOCOL(q->flags); + q->answer_family = SYNTHESIZE_FAMILY(q->flags); *state = DNS_TRANSACTION_SUCCESS; @@ -609,9 +867,8 @@ static int dns_query_synthesize_reply(DnsQuery *q, DnsTransactionState *state) { int dns_query_go(DnsQuery *q) { DnsScopeMatch found = DNS_SCOPE_NO; DnsScope *s, *first = NULL; - DnsTransaction *t; + DnsQueryCandidate *c; const char *name; - Iterator i; int r; assert(q); @@ -622,7 +879,7 @@ int dns_query_go(DnsQuery *q) { assert(q->question); assert(q->question->n_keys > 0); - name = DNS_RESOURCE_KEY_NAME(q->question->keys[0]); + name = dns_question_first_name(q->question); LIST_FOREACH(scopes, s, q->manager->dns_scopes) { DnsScopeMatch match; @@ -655,7 +912,7 @@ int dns_query_go(DnsQuery *q) { return 1; } - r = dns_query_add_transaction_split(q, first); + r = dns_query_add_candidate(q, first); if (r < 0) goto fail; @@ -669,7 +926,7 @@ int dns_query_go(DnsQuery *q) { if (match != found) continue; - r = dns_query_add_transaction_split(q, s); + r = dns_query_add_candidate(q, s); if (r < 0) goto fail; } @@ -691,14 +948,13 @@ int dns_query_go(DnsQuery *q) { q->state = DNS_TRANSACTION_PENDING; q->block_ready++; - /* Start the transactions that are not started yet */ - SET_FOREACH(t, q->transactions, i) { - if (t->state != DNS_TRANSACTION_NULL) - continue; - - r = dns_transaction_go(t); - if (r < 0) + /* Start the transactions */ + LIST_FOREACH(candidates_by_query, c, q->candidates) { + r = dns_query_candidate_go(c); + if (r < 0) { + q->block_ready--; goto fail; + } } q->block_ready--; @@ -711,132 +967,128 @@ fail: return r; } -void dns_query_ready(DnsQuery *q) { - DnsTransaction *t; +static void dns_query_accept(DnsQuery *q, DnsQueryCandidate *c) { DnsTransactionState state = DNS_TRANSACTION_NO_SERVERS; - _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL; - int rcode = 0; - DnsScope *scope = NULL; - bool pending = false; + DnsTransaction *t; Iterator i; assert(q); - assert(IN_SET(q->state, DNS_TRANSACTION_NULL, DNS_TRANSACTION_PENDING)); - /* Note that this call might invalidate the query. Callers - * should hence not attempt to access the query or transaction - * after calling this function, unless the block_ready - * counter was explicitly bumped before doing so. */ - - if (q->block_ready > 0) + if (!c) { + dns_query_synthesize_reply(q, &state); + dns_query_complete(q, state); return; + } - SET_FOREACH(t, q->transactions, i) { + SET_FOREACH(t, c->transactions, i) { - /* If we found a successful answer, ignore all answers from other scopes */ - if (state == DNS_TRANSACTION_SUCCESS && t->scope != scope) - continue; + switch (t->state) { - /* One of the transactions is still going on, let's maybe wait for it */ - if (IN_SET(t->state, DNS_TRANSACTION_PENDING, DNS_TRANSACTION_NULL)) { - pending = true; - continue; - } + case DNS_TRANSACTION_SUCCESS: { + /* We found a successfuly reply, merge it into the answer */ + DnsAnswer *merged; - /* One of the transactions is successful, let's use - * it, and copy its data out */ - if (t->state == DNS_TRANSACTION_SUCCESS) { - DnsAnswer *a; - - if (t->received) { - rcode = DNS_PACKET_RCODE(t->received); - a = t->received->answer; - } else { - rcode = t->cached_rcode; - a = t->cached; + merged = dns_answer_merge(q->answer, t->answer); + if (!merged) { + dns_query_complete(q, DNS_TRANSACTION_RESOURCES); + return; } - if (state == DNS_TRANSACTION_SUCCESS) { - DnsAnswer *merged; + dns_answer_unref(q->answer); + q->answer = merged; + q->answer_rcode = t->answer_rcode; + + state = DNS_TRANSACTION_SUCCESS; + break; + } + + case DNS_TRANSACTION_PENDING: + case DNS_TRANSACTION_NULL: + case DNS_TRANSACTION_ABORTED: + /* Ignore transactions that didn't complete */ + continue; + + default: + /* Any kind of failure? Store the data away, + * if there's nothing stored yet. */ - merged = dns_answer_merge(answer, a); - if (!merged) { - dns_query_complete(q, DNS_TRANSACTION_RESOURCES); - return; - } + if (state != DNS_TRANSACTION_SUCCESS) { - dns_answer_unref(answer); - answer = merged; - } else { - dns_answer_unref(answer); - answer = dns_answer_ref(a); + dns_answer_unref(q->answer); + q->answer = dns_answer_ref(t->answer); + q->answer_rcode = t->answer_rcode; + + state = t->state; } - scope = t->scope; - state = DNS_TRANSACTION_SUCCESS; - continue; + break; } + } - /* One of the transactions has failed, let's see - * whether we find anything better, but if not, return - * its response data */ - if (state != DNS_TRANSACTION_SUCCESS && t->state == DNS_TRANSACTION_FAILURE) { - DnsAnswer *a; - - if (t->received) { - rcode = DNS_PACKET_RCODE(t->received); - a = t->received->answer; - } else { - rcode = t->cached_rcode; - a = t->cached; - } + q->answer_protocol = c->scope->protocol; + q->answer_family = c->scope->family; - dns_answer_unref(answer); - answer = dns_answer_ref(a); + dns_search_domain_unref(q->answer_search_domain); + q->answer_search_domain = dns_search_domain_ref(c->search_domain); - scope = t->scope; - state = DNS_TRANSACTION_FAILURE; - continue; - } + dns_query_synthesize_reply(q, &state); + dns_query_complete(q, state); +} - if (state == DNS_TRANSACTION_NO_SERVERS && t->state != DNS_TRANSACTION_NO_SERVERS) - state = t->state; - } +void dns_query_ready(DnsQuery *q) { + + DnsQueryCandidate *bad = NULL, *c; + bool pending = false; - if (pending) { + assert(q); + assert(IN_SET(q->state, DNS_TRANSACTION_NULL, DNS_TRANSACTION_PENDING)); - /* If so far we weren't successful, and there's - * something still pending, then wait for it */ - if (state != DNS_TRANSACTION_SUCCESS) + /* Note that this call might invalidate the query. Callers + * should hence not attempt to access the query or transaction + * after calling this function, unless the block_ready + * counter was explicitly bumped before doing so. */ + + if (q->block_ready > 0) + return; + + LIST_FOREACH(candidates_by_query, c, q->candidates) { + DnsTransactionState state; + + state = dns_query_candidate_state(c); + switch (state) { + + case DNS_TRANSACTION_SUCCESS: + /* One of the transactions is successful, + * let's use it, and copy its data out */ + dns_query_accept(q, c); return; - /* If we already were successful, then only wait for - * other transactions on the same scope to finish. */ - SET_FOREACH(t, q->transactions, i) { - if (t->scope == scope && IN_SET(t->state, DNS_TRANSACTION_PENDING, DNS_TRANSACTION_NULL)) - return; - } - } + case DNS_TRANSACTION_PENDING: + case DNS_TRANSACTION_NULL: + /* One of the transactions is still going on, let's maybe wait for it */ + pending = true; + break; - if (IN_SET(state, DNS_TRANSACTION_SUCCESS, DNS_TRANSACTION_FAILURE)) { - q->answer = dns_answer_ref(answer); - q->answer_rcode = rcode; - q->answer_protocol = scope ? scope->protocol : _DNS_PROTOCOL_INVALID; - q->answer_family = scope ? scope->family : AF_UNSPEC; + default: + /* Any kind of failure */ + bad = c; + break; + } } - /* Try to synthesize a reply if we couldn't resolve something. */ - dns_query_synthesize_reply(q, &state); + if (pending) + return; - dns_query_complete(q, state); + dns_query_accept(q, bad); } -int dns_query_cname_redirect(DnsQuery *q, const DnsResourceRecord *cname) { +static int dns_query_cname_redirect(DnsQuery *q, const DnsResourceRecord *cname) { _cleanup_(dns_question_unrefp) DnsQuestion *nq = NULL; int r; assert(q); + q->n_cname_redirects ++; if (q->n_cname_redirects > CNAME_MAX) return -ELOOP; @@ -848,14 +1100,66 @@ int dns_query_cname_redirect(DnsQuery *q, const DnsResourceRecord *cname) { q->question = nq; nq = NULL; - q->n_cname_redirects++; - dns_query_stop(q); q->state = DNS_TRANSACTION_NULL; return 0; } +int dns_query_process_cname(DnsQuery *q) { + _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *cname = NULL; + DnsResourceRecord *rr; + int r; + + assert(q); + + if (q->state != DNS_TRANSACTION_SUCCESS) + return 0; + + DNS_ANSWER_FOREACH(rr, q->answer) { + + r = dns_question_matches_rr(q->question, rr, DNS_SEARCH_DOMAIN_NAME(q->answer_search_domain)); + if (r < 0) + return r; + if (r > 0) + return 0; /* The answer matches directly, no need to follow cnames */ + + r = dns_question_matches_cname(q->question, rr, DNS_SEARCH_DOMAIN_NAME(q->answer_search_domain)); + if (r < 0) + return r; + if (r > 0 && !cname) + cname = dns_resource_record_ref(rr); + } + + if (!cname) + return 0; /* No cname to follow */ + + if (q->flags & SD_RESOLVED_NO_CNAME) + return -ELOOP; + + /* OK, let's actually follow the CNAME */ + r = dns_query_cname_redirect(q, cname); + if (r < 0) + return r; + + /* Let's see if the answer can already answer the new + * redirected question */ + DNS_ANSWER_FOREACH(rr, q->answer) { + r = dns_question_matches_rr(q->question, rr, NULL); + if (r < 0) + return r; + if (r > 0) + return 0; /* It can answer it, yay! */ + } + + /* OK, it cannot, let's begin with the new query */ + r = dns_query_go(q); + if (r < 0) + return r; + + return 1; /* We return > 0, if we restarted the query for a new cname */ +} + static int on_bus_track(sd_bus_track *t, void *userdata) { DnsQuery *q = userdata; diff --git a/src/resolve/resolved-dns-query.h b/src/resolve/resolved-dns-query.h index e7063d9678..a9d7904a8d 100644 --- a/src/resolve/resolved-dns-query.h +++ b/src/resolve/resolved-dns-query.h @@ -23,56 +23,88 @@ #include "sd-bus.h" + #include "set.h" +typedef struct DnsQueryCandidate DnsQueryCandidate; typedef struct DnsQuery DnsQuery; -#include "resolved-dns-question.h" #include "resolved-dns-answer.h" +#include "resolved-dns-question.h" #include "resolved-dns-stream.h" +#include "resolved-dns-search-domain.h" + +struct DnsQueryCandidate { + DnsQuery *query; + DnsScope *scope; + + DnsSearchDomain *search_domain; + + int error_code; + Set *transactions; + + LIST_FIELDS(DnsQueryCandidate, candidates_by_query); + LIST_FIELDS(DnsQueryCandidate, candidates_by_scope); +}; struct DnsQuery { Manager *manager; - DnsQuestion *question; + /* When resolving a service, we first create a TXT+SRV query, + * and then for the hostnames we discover auxiliary A+AAAA + * queries. This pointer always points from the auxiliary + * queries back to the TXT+SRV query. */ + DnsQuery *auxiliary_for; + LIST_HEAD(DnsQuery, auxiliary_queries); + unsigned n_auxiliary_queries; + int auxiliary_result; + + DnsQuestion *question; uint64_t flags; int ifindex; DnsTransactionState state; unsigned n_cname_redirects; + LIST_HEAD(DnsQueryCandidate, candidates); sd_event_source *timeout_event_source; /* Discovered data */ DnsAnswer *answer; - int answer_family; - DnsProtocol answer_protocol; int answer_rcode; + DnsProtocol answer_protocol; + int answer_family; + DnsSearchDomain *answer_search_domain; /* Bus client information */ sd_bus_message *request; int request_family; - const char *request_hostname; + bool request_address_valid; union in_addr_union request_address; + unsigned block_all_complete; /* Completion callback */ void (*complete)(DnsQuery* q); unsigned block_ready; - Set *transactions; - sd_bus_track *bus_track; LIST_FIELDS(DnsQuery, queries); + LIST_FIELDS(DnsQuery, auxiliary_queries); }; +DnsQueryCandidate* dns_query_candidate_free(DnsQueryCandidate *c); +void dns_query_candidate_ready(DnsQueryCandidate *c); + int dns_query_new(Manager *m, DnsQuery **q, DnsQuestion *question, int family, uint64_t flags); DnsQuery *dns_query_free(DnsQuery *q); +int dns_query_make_auxiliary(DnsQuery *q, DnsQuery *auxiliary_for); + int dns_query_go(DnsQuery *q); void dns_query_ready(DnsQuery *q); -int dns_query_cname_redirect(DnsQuery *q, const DnsResourceRecord *cname); +int dns_query_process_cname(DnsQuery *q); int dns_query_bus_track(DnsQuery *q, sd_bus_message *m); diff --git a/src/resolve/resolved-dns-question.c b/src/resolve/resolved-dns-question.c index 48951221dc..3249448d3b 100644 --- a/src/resolve/resolved-dns-question.c +++ b/src/resolve/resolved-dns-question.c @@ -89,7 +89,7 @@ int dns_question_add(DnsQuestion *q, DnsResourceKey *key) { return 0; } -int dns_question_matches_rr(DnsQuestion *q, DnsResourceRecord *rr) { +int dns_question_matches_rr(DnsQuestion *q, DnsResourceRecord *rr, const char *search_domain) { unsigned i; int r; @@ -99,7 +99,7 @@ int dns_question_matches_rr(DnsQuestion *q, DnsResourceRecord *rr) { return 0; for (i = 0; i < q->n_keys; i++) { - r = dns_resource_key_match_rr(q->keys[i], rr); + r = dns_resource_key_match_rr(q->keys[i], rr, search_domain); if (r != 0) return r; } @@ -107,7 +107,7 @@ int dns_question_matches_rr(DnsQuestion *q, DnsResourceRecord *rr) { return 0; } -int dns_question_matches_cname(DnsQuestion *q, DnsResourceRecord *rr) { +int dns_question_matches_cname(DnsQuestion *q, DnsResourceRecord *rr, const char *search_domain) { unsigned i; int r; @@ -117,7 +117,7 @@ int dns_question_matches_cname(DnsQuestion *q, DnsResourceRecord *rr) { return 0; for (i = 0; i < q->n_keys; i++) { - r = dns_resource_key_match_cname(q->keys[i], rr); + r = dns_resource_key_match_cname(q->keys[i], rr, search_domain); if (r != 0) return r; } @@ -125,7 +125,7 @@ int dns_question_matches_cname(DnsQuestion *q, DnsResourceRecord *rr) { return 0; } -int dns_question_is_valid(DnsQuestion *q) { +int dns_question_is_valid_for_query(DnsQuestion *q) { const char *name; unsigned i; int r; @@ -155,50 +155,6 @@ int dns_question_is_valid(DnsQuestion *q) { return 1; } -int dns_question_is_superset(DnsQuestion *q, DnsQuestion *other) { - unsigned j; - int r; - - /* Checks if all keys in "other" are also contained in "q" */ - - if (!other) - return 1; - - for (j = 0; j < other->n_keys; j++) { - DnsResourceKey *b = other->keys[j]; - bool found = false; - unsigned i; - - if (!q) - return 0; - - for (i = 0; i < q->n_keys; i++) { - DnsResourceKey *a = q->keys[i]; - - r = dns_name_equal(DNS_RESOURCE_KEY_NAME(a), DNS_RESOURCE_KEY_NAME(b)); - if (r < 0) - return r; - - if (r == 0) - continue; - - if (a->class != b->class && a->class != DNS_CLASS_ANY) - continue; - - if (a->type != b->type && a->type != DNS_TYPE_ANY) - continue; - - found = true; - break; - } - - if (!found) - return 0; - } - - return 1; -} - int dns_question_contains(DnsQuestion *a, DnsResourceKey *k) { unsigned j; int r; @@ -251,6 +207,7 @@ int dns_question_cname_redirect(DnsQuestion *q, const DnsResourceRecord *cname, assert(cname); assert(ret); + assert(IN_SET(cname->key->type, DNS_TYPE_CNAME, DNS_TYPE_DNAME)); if (!q) { n = dns_question_new(0); @@ -263,7 +220,22 @@ int dns_question_cname_redirect(DnsQuestion *q, const DnsResourceRecord *cname, } for (i = 0; i < q->n_keys; i++) { - r = dns_name_equal(DNS_RESOURCE_KEY_NAME(q->keys[i]), cname->cname.name); + _cleanup_free_ char *destination = NULL; + const char *d; + + if (cname->key->type == DNS_TYPE_CNAME) + d = cname->cname.name; + else { + r = dns_name_change_suffix(DNS_RESOURCE_KEY_NAME(q->keys[i]), DNS_RESOURCE_KEY_NAME(cname->key), cname->dname.name, &destination); + if (r < 0) + return r; + if (r == 0) + continue; + + d = destination; + } + + r = dns_name_equal(DNS_RESOURCE_KEY_NAME(q->keys[i]), d); if (r < 0) return r; @@ -301,3 +273,131 @@ int dns_question_cname_redirect(DnsQuestion *q, const DnsResourceRecord *cname, return 1; } + +const char *dns_question_first_name(DnsQuestion *q) { + + if (!q) + return NULL; + + if (q->n_keys < 1) + return NULL; + + return DNS_RESOURCE_KEY_NAME(q->keys[0]); +} + +int dns_question_new_address(DnsQuestion **ret, int family, const char *name) { + _cleanup_(dns_question_unrefp) DnsQuestion *q = NULL; + int r; + + assert(ret); + assert(name); + + if (!IN_SET(family, AF_INET, AF_INET6, AF_UNSPEC)) + return -EAFNOSUPPORT; + + q = dns_question_new(family == AF_UNSPEC ? 2 : 1); + if (!q) + return -ENOMEM; + + if (family != AF_INET6) { + _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL; + + key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, name); + if (!key) + return -ENOMEM; + + r = dns_question_add(q, key); + if (r < 0) + return r; + } + + if (family != AF_INET) { + _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL; + + key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_AAAA, name); + if (!key) + return -ENOMEM; + + r = dns_question_add(q, key); + if (r < 0) + return r; + } + + *ret = q; + q = NULL; + + return 0; +} + +int dns_question_new_reverse(DnsQuestion **ret, int family, const union in_addr_union *a) { + _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL; + _cleanup_(dns_question_unrefp) DnsQuestion *q = NULL; + _cleanup_free_ char *reverse = NULL; + int r; + + assert(ret); + assert(a); + + if (!IN_SET(family, AF_INET, AF_INET6, AF_UNSPEC)) + return -EAFNOSUPPORT; + + r = dns_name_reverse(family, a, &reverse); + if (r < 0) + return r; + + q = dns_question_new(1); + if (!q) + return -ENOMEM; + + key = dns_resource_key_new_consume(DNS_CLASS_IN, DNS_TYPE_PTR, reverse); + if (!key) + return -ENOMEM; + + reverse = NULL; + + r = dns_question_add(q, key); + if (r < 0) + return r; + + *ret = q; + q = NULL; + + return 0; +} + +int dns_question_new_service(DnsQuestion **ret, const char *name, bool with_txt) { + _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL; + _cleanup_(dns_question_unrefp) DnsQuestion *q = NULL; + int r; + + assert(ret); + assert(name); + + q = dns_question_new(1 + with_txt); + if (!q) + return -ENOMEM; + + key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_SRV, name); + if (!key) + return -ENOMEM; + + r = dns_question_add(q, key); + if (r < 0) + return r; + + if (with_txt) { + dns_resource_key_unref(key); + key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_TXT, name); + if (!key) + return -ENOMEM; + + r = dns_question_add(q, key); + if (r < 0) + return r; + } + + *ret = q; + q = NULL; + + return 0; +} diff --git a/src/resolve/resolved-dns-question.h b/src/resolve/resolved-dns-question.h index 13cd1f20f3..e77116c03a 100644 --- a/src/resolve/resolved-dns-question.h +++ b/src/resolve/resolved-dns-question.h @@ -37,15 +37,28 @@ DnsQuestion *dns_question_new(unsigned n); DnsQuestion *dns_question_ref(DnsQuestion *q); DnsQuestion *dns_question_unref(DnsQuestion *q); +int dns_question_new_address(DnsQuestion **ret, int family, const char *name); +int dns_question_new_reverse(DnsQuestion **ret, int family, const union in_addr_union *a); +int dns_question_new_service(DnsQuestion **ret, const char *name, bool with_txt); + int dns_question_add(DnsQuestion *q, DnsResourceKey *key); -int dns_question_matches_rr(DnsQuestion *q, DnsResourceRecord *rr); -int dns_question_matches_cname(DnsQuestion *q, DnsResourceRecord *rr); -int dns_question_is_valid(DnsQuestion *q); -int dns_question_is_superset(DnsQuestion *q, DnsQuestion *other); +int dns_question_matches_rr(DnsQuestion *q, DnsResourceRecord *rr, const char *search_domain); +int dns_question_matches_cname(DnsQuestion *q, DnsResourceRecord *rr, const char* search_domain); +int dns_question_is_valid_for_query(DnsQuestion *q); int dns_question_contains(DnsQuestion *a, DnsResourceKey *k); int dns_question_is_equal(DnsQuestion *a, DnsQuestion *b); int dns_question_cname_redirect(DnsQuestion *q, const DnsResourceRecord *cname, DnsQuestion **ret); +const char *dns_question_first_name(DnsQuestion *q); + DEFINE_TRIVIAL_CLEANUP_FUNC(DnsQuestion*, dns_question_unref); + +#define DNS_QUESTION_FOREACH(key, q) \ + for (unsigned _i = ({ \ + (key) = ((q) && (q)->n_keys > 0) ? (q)->keys[0] : NULL; \ + 0; \ + }); \ + (q) && ((_i) < (q)->n_keys); \ + _i++, (key) = (_i < (q)->n_keys ? (q)->keys[_i] : NULL)) diff --git a/src/resolve/resolved-dns-rr.c b/src/resolve/resolved-dns-rr.c index ba2ea686f3..4a1abb0cdc 100644 --- a/src/resolve/resolved-dns-rr.c +++ b/src/resolve/resolved-dns-rr.c @@ -57,10 +57,61 @@ DnsResourceKey* dns_resource_key_new_cname(const DnsResourceKey *key) { } DnsResourceKey* dns_resource_key_new_redirect(const DnsResourceKey *key, const DnsResourceRecord *cname) { + int r; + assert(key); assert(cname); - return dns_resource_key_new(key->class, key->type, cname->cname.name); + assert(IN_SET(cname->key->type, DNS_TYPE_CNAME, DNS_TYPE_DNAME)); + + if (cname->key->type == DNS_TYPE_CNAME) + return dns_resource_key_new(key->class, key->type, cname->cname.name); + else { + DnsResourceKey *k; + char *destination = NULL; + + r = dns_name_change_suffix(DNS_RESOURCE_KEY_NAME(key), DNS_RESOURCE_KEY_NAME(cname->key), cname->dname.name, &destination); + if (r < 0) + return NULL; + if (r == 0) + return dns_resource_key_ref((DnsResourceKey*) key); + + k = dns_resource_key_new_consume(key->class, key->type, destination); + if (!k) { + free(destination); + return NULL; + } + + return k; + } +} + +int dns_resource_key_new_append_suffix(DnsResourceKey **ret, DnsResourceKey *key, char *name) { + DnsResourceKey *new_key; + char *joined; + int r; + + assert(ret); + assert(key); + assert(name); + + if (dns_name_is_root(name)) { + *ret = dns_resource_key_ref(key); + return 0; + } + + r = dns_name_concat(DNS_RESOURCE_KEY_NAME(key), name, &joined); + if (r < 0) + return r; + + new_key = dns_resource_key_new_consume(key->class, key->type, joined); + if (!new_key) { + free(joined); + return -ENOMEM; + } + + *ret = new_key; + return 0; } DnsResourceKey* dns_resource_key_new_consume(uint16_t class, uint16_t type, char *name) { @@ -122,30 +173,73 @@ int dns_resource_key_equal(const DnsResourceKey *a, const DnsResourceKey *b) { return 1; } -int dns_resource_key_match_rr(const DnsResourceKey *key, const DnsResourceRecord *rr) { +int dns_resource_key_match_rr(const DnsResourceKey *key, const DnsResourceRecord *rr, const char *search_domain) { + int r; + assert(key); assert(rr); + /* Checks if an rr matches the specified key. If a search + * domain is specified, it will also be checked if the key + * with the search domain suffixed might match the RR. */ + if (rr->key->class != key->class && key->class != DNS_CLASS_ANY) return 0; if (rr->key->type != key->type && key->type != DNS_TYPE_ANY) return 0; - return dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), DNS_RESOURCE_KEY_NAME(key)); + r = dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), DNS_RESOURCE_KEY_NAME(key)); + if (r != 0) + return r; + + if (search_domain) { + _cleanup_free_ char *joined = NULL; + + r = dns_name_concat(DNS_RESOURCE_KEY_NAME(key), search_domain, &joined); + if (r < 0) + return r; + + return dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), joined); + } + + return 0; } -int dns_resource_key_match_cname(const DnsResourceKey *key, const DnsResourceRecord *rr) { +int dns_resource_key_match_cname(const DnsResourceKey *key, const DnsResourceRecord *rr, const char *search_domain) { + int r; + assert(key); assert(rr); if (rr->key->class != key->class && key->class != DNS_CLASS_ANY) return 0; - if (rr->key->type != DNS_TYPE_CNAME) + if (rr->key->type == DNS_TYPE_CNAME) + r = dns_name_equal(DNS_RESOURCE_KEY_NAME(key), DNS_RESOURCE_KEY_NAME(rr->key)); + else if (rr->key->type == DNS_TYPE_DNAME) + r = dns_name_endswith(DNS_RESOURCE_KEY_NAME(key), DNS_RESOURCE_KEY_NAME(rr->key)); + else return 0; - return dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), DNS_RESOURCE_KEY_NAME(key)); + if (r != 0) + return r; + + if (search_domain) { + _cleanup_free_ char *joined = NULL; + + r = dns_name_concat(DNS_RESOURCE_KEY_NAME(key), search_domain, &joined); + if (r < 0) + return r; + + if (rr->key->type == DNS_TYPE_CNAME) + return dns_name_equal(joined, DNS_RESOURCE_KEY_NAME(rr->key)); + else if (rr->key->type == DNS_TYPE_DNAME) + return dns_name_endswith(joined, DNS_RESOURCE_KEY_NAME(rr->key)); + } + + return 0; + } static void dns_resource_key_hash_func(const void *i, struct siphash *state) { @@ -273,7 +367,7 @@ DnsResourceRecord* dns_resource_record_unref(DnsResourceRecord *rr) { case DNS_TYPE_TXT: case DNS_TYPE_SPF: - strv_free(rr->txt.strings); + dns_txt_item_free_all(rr->txt.items); break; case DNS_TYPE_SOA: @@ -430,7 +524,7 @@ int dns_resource_record_equal(const DnsResourceRecord *a, const DnsResourceRecor case DNS_TYPE_SPF: /* exactly the same as TXT */ case DNS_TYPE_TXT: - return strv_equal(a->txt.strings, b->txt.strings); + return dns_txt_item_equal(a->txt.items, b->txt.items); case DNS_TYPE_A: return memcmp(&a->a.in_addr, &b->a.in_addr, sizeof(struct in_addr)) == 0; @@ -600,6 +694,43 @@ static char *format_types(Bitmap *types) { return strjoin("( ", str, " )", NULL); } +static char *format_txt(DnsTxtItem *first) { + DnsTxtItem *i; + size_t c = 1; + char *p, *s; + + LIST_FOREACH(items, i, first) + c += i->length * 4 + 3; + + p = s = new(char, c); + if (!s) + return NULL; + + LIST_FOREACH(items, i, first) { + size_t j; + + if (i != first) + *(p++) = ' '; + + *(p++) = '"'; + + for (j = 0; j < i->length; j++) { + if (i->data[j] < ' ' || i->data[j] == '"' || i->data[j] >= 127) { + *(p++) = '\\'; + *(p++) = '0' + (i->data[j] / 100); + *(p++) = '0' + ((i->data[j] / 10) % 10); + *(p++) = '0' + (i->data[j] % 10); + } else + *(p++) = i->data[j]; + } + + *(p++) = '"'; + } + + *p = 0; + return s; +} + int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret) { _cleanup_free_ char *k = NULL, *t = NULL; char *s; @@ -642,14 +773,13 @@ int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret) { case DNS_TYPE_SPF: /* exactly the same as TXT */ case DNS_TYPE_TXT: - t = strv_join_quoted(rr->txt.strings); + t = format_txt(rr->txt.items); if (!t) return -ENOMEM; s = strjoin(k, " ", t, NULL); if (!s) return -ENOMEM; - break; case DNS_TYPE_A: { @@ -890,3 +1020,32 @@ int dns_class_from_string(const char *s, uint16_t *class) { return 0; } + +DnsTxtItem *dns_txt_item_free_all(DnsTxtItem *i) { + DnsTxtItem *n; + + if (!i) + return NULL; + + n = i->items_next; + + free(i); + return dns_txt_item_free_all(n); +} + +bool dns_txt_item_equal(DnsTxtItem *a, DnsTxtItem *b) { + + if (!a != !b) + return false; + + if (!a) + return true; + + if (a->length != b->length) + return false; + + if (memcmp(a->data, b->data, a->length) != 0) + return false; + + return dns_txt_item_equal(a->items_next, b->items_next); +} diff --git a/src/resolve/resolved-dns-rr.h b/src/resolve/resolved-dns-rr.h index 9e2207c0aa..f8066c06a6 100644 --- a/src/resolve/resolved-dns-rr.h +++ b/src/resolve/resolved-dns-rr.h @@ -24,12 +24,14 @@ #include <netinet/in.h> #include "bitmap.h" +#include "dns-type.h" #include "hashmap.h" #include "in-addr-util.h" -#include "dns-type.h" +#include "list.h" typedef struct DnsResourceKey DnsResourceKey; typedef struct DnsResourceRecord DnsResourceRecord; +typedef struct DnsTxtItem DnsTxtItem; /* DNS record classes, see RFC 1035 */ enum { @@ -45,6 +47,12 @@ struct DnsResourceKey { char *_name; /* don't access directy, use DNS_RESOURCE_KEY_NAME()! */ }; +struct DnsTxtItem { + size_t length; + LIST_FIELDS(DnsTxtItem, items); + uint8_t data[]; +}; + struct DnsResourceRecord { unsigned n_ref; DnsResourceKey *key; @@ -73,7 +81,7 @@ struct DnsResourceRecord { } hinfo; struct { - char **strings; + DnsTxtItem *items; } txt, spf; struct { @@ -178,13 +186,15 @@ static inline const char* DNS_RESOURCE_KEY_NAME(const DnsResourceKey *key) { DnsResourceKey* dns_resource_key_new(uint16_t class, uint16_t type, const char *name); DnsResourceKey* dns_resource_key_new_cname(const DnsResourceKey *key); +DnsResourceKey* dns_resource_key_new_dname(const DnsResourceKey *key); DnsResourceKey* dns_resource_key_new_redirect(const DnsResourceKey *key, const DnsResourceRecord *cname); +int dns_resource_key_new_append_suffix(DnsResourceKey **ret, DnsResourceKey *key, char *name); DnsResourceKey* dns_resource_key_new_consume(uint16_t class, uint16_t type, char *name); DnsResourceKey* dns_resource_key_ref(DnsResourceKey *key); DnsResourceKey* dns_resource_key_unref(DnsResourceKey *key); int dns_resource_key_equal(const DnsResourceKey *a, const DnsResourceKey *b); -int dns_resource_key_match_rr(const DnsResourceKey *key, const DnsResourceRecord *rr); -int dns_resource_key_match_cname(const DnsResourceKey *key, const DnsResourceRecord *rr); +int dns_resource_key_match_rr(const DnsResourceKey *key, const DnsResourceRecord *rr, const char *search_domain); +int dns_resource_key_match_cname(const DnsResourceKey *key, const DnsResourceRecord *rr, const char *search_domain); int dns_resource_key_to_string(const DnsResourceKey *key, char **ret); DEFINE_TRIVIAL_CLEANUP_FUNC(DnsResourceKey*, dns_resource_key_unref); @@ -198,6 +208,9 @@ int dns_resource_record_equal(const DnsResourceRecord *a, const DnsResourceRecor int dns_resource_record_to_string(const DnsResourceRecord *rr, char **ret); DEFINE_TRIVIAL_CLEANUP_FUNC(DnsResourceRecord*, dns_resource_record_unref); +DnsTxtItem *dns_txt_item_free_all(DnsTxtItem *i); +bool dns_txt_item_equal(DnsTxtItem *a, DnsTxtItem *b); + const char *dns_class_to_string(uint16_t type); int dns_class_from_string(const char *name, uint16_t *class); diff --git a/src/resolve/resolved-dns-scope.c b/src/resolve/resolved-dns-scope.c index b15370b017..fc4ae57ce0 100644 --- a/src/resolve/resolved-dns-scope.c +++ b/src/resolve/resolved-dns-scope.c @@ -69,18 +69,12 @@ int dns_scope_new(Manager *m, DnsScope **ret, Link *l, DnsProtocol protocol, int return 0; } -DnsScope* dns_scope_free(DnsScope *s) { - DnsTransaction *t; - DnsResourceRecord *rr; - - if (!s) - return NULL; - - log_debug("Removing scope on link %s, protocol %s, family %s", s->link ? s->link->name : "*", dns_protocol_to_string(s->protocol), s->family == AF_UNSPEC ? "*" : af_to_name(s->family)); +static void dns_scope_abort_transactions(DnsScope *s) { + assert(s); - dns_scope_llmnr_membership(s, false); + while (s->transactions) { + DnsTransaction *t = s->transactions; - while ((t = hashmap_steal_first(s->transactions))) { /* Abort the transaction, but make sure it is not * freed while we still look at it */ @@ -90,8 +84,23 @@ DnsScope* dns_scope_free(DnsScope *s) { dns_transaction_free(t); } +} - hashmap_free(s->transactions); +DnsScope* dns_scope_free(DnsScope *s) { + DnsResourceRecord *rr; + + if (!s) + return NULL; + + log_debug("Removing scope on link %s, protocol %s, family %s", s->link ? s->link->name : "*", dns_protocol_to_string(s->protocol), s->family == AF_UNSPEC ? "*" : af_to_name(s->family)); + + dns_scope_llmnr_membership(s, false); + dns_scope_abort_transactions(s); + + while (s->query_candidates) + dns_query_candidate_free(s->query_candidates); + + hashmap_free(s->transactions_by_key); while ((rr = ordered_hashmap_steal_first(s->conflict_queue))) dns_resource_record_unref(rr); @@ -103,7 +112,6 @@ DnsScope* dns_scope_free(DnsScope *s) { dns_zone_flush(&s->zone); LIST_REMOVE(scopes, s->manager->dns_scopes, s); - strv_free(s->domains); free(s); return NULL; @@ -136,11 +144,11 @@ void dns_scope_next_dns_server(DnsScope *s) { void dns_scope_packet_received(DnsScope *s, usec_t rtt) { assert(s); - if (rtt > s->max_rtt) { - s->max_rtt = rtt; - s->resend_timeout = MIN(MAX(MULTICAST_RESEND_TIMEOUT_MIN_USEC, s->max_rtt * 2), - MULTICAST_RESEND_TIMEOUT_MAX_USEC); - } + if (rtt <= s->max_rtt) + return; + + s->max_rtt = rtt; + s->resend_timeout = MIN(MAX(MULTICAST_RESEND_TIMEOUT_MIN_USEC, s->max_rtt * 2), MULTICAST_RESEND_TIMEOUT_MAX_USEC); } void dns_scope_packet_lost(DnsScope *s, usec_t usec) { @@ -323,7 +331,7 @@ int dns_scope_tcp_socket(DnsScope *s, int family, const union in_addr_union *add } DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, const char *domain) { - char **i; + DnsSearchDomain *d; assert(s); assert(domain); @@ -334,7 +342,7 @@ DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, co if ((SD_RESOLVED_FLAGS_MAKE(s->protocol, s->family) & flags) == 0) return DNS_SCOPE_NO; - if (dns_name_root(domain) != 0) + if (dns_name_is_root(domain)) return DNS_SCOPE_NO; /* Never resolve any loopback hostname or IP address via DNS, @@ -345,15 +353,22 @@ DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, co dns_name_equal(domain, "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa") > 0) return DNS_SCOPE_NO; - STRV_FOREACH(i, s->domains) - if (dns_name_endswith(domain, *i) > 0) + /* Always honour search domains for routing queries. Note that + * we return DNS_SCOPE_YES here, rather than just + * DNS_SCOPE_MAYBE, which means wildcard scopes won't be + * considered anymore. */ + LIST_FOREACH(domains, d, dns_scope_get_search_domains(s)) + if (dns_name_endswith(domain, d->name) > 0) return DNS_SCOPE_YES; switch (s->protocol) { + case DNS_PROTOCOL_DNS: - if (dns_name_endswith(domain, "254.169.in-addr.arpa") == 0 && - dns_name_endswith(domain, "0.8.e.f.ip6.arpa") == 0 && - dns_name_single_label(domain) == 0) + + if ((!dns_name_is_single_label(domain) || + (!(flags & SD_RESOLVED_NO_SEARCH) && dns_scope_has_search_domains(s))) && + dns_name_endswith(domain, "254.169.in-addr.arpa") == 0 && + dns_name_endswith(domain, "0.8.e.f.ip6.arpa") == 0) return DNS_SCOPE_MAYBE; return DNS_SCOPE_NO; @@ -371,7 +386,7 @@ DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, co case DNS_PROTOCOL_LLMNR: if ((s->family == AF_INET && dns_name_endswith(domain, "in-addr.arpa") > 0) || (s->family == AF_INET6 && dns_name_endswith(domain, "ip6.arpa") > 0) || - (dns_name_single_label(domain) > 0 && /* only resolve single label names via LLMNR */ + (dns_name_is_single_label(domain) && /* only resolve single label names via LLMNR */ !is_gateway_hostname(domain) && /* don't resolve "gateway" with LLMNR, let nss-myhostname handle this */ manager_is_own_hostname(s->manager, domain) <= 0)) /* never resolve the local hostname via LLMNR */ return DNS_SCOPE_MAYBE; @@ -543,6 +558,7 @@ static void dns_scope_verify_conflicts(DnsScope *s, DnsPacket *p) { void dns_scope_process_query(DnsScope *s, DnsStream *stream, DnsPacket *p) { _cleanup_(dns_packet_unrefp) DnsPacket *reply = NULL; _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL, *soa = NULL; + DnsResourceKey *key = NULL; bool tentative = false; int r, fd; @@ -576,7 +592,10 @@ void dns_scope_process_query(DnsScope *s, DnsStream *stream, DnsPacket *p) { return; } - r = dns_zone_lookup(&s->zone, p->question, &answer, &soa, &tentative); + assert(p->question->n_keys == 1); + key = p->question->keys[0]; + + r = dns_zone_lookup(&s->zone, key, &answer, &soa, &tentative); if (r < 0) { log_debug_errno(r, "Failed to lookup key: %m"); return; @@ -634,7 +653,7 @@ DnsTransaction *dns_scope_find_transaction(DnsScope *scope, DnsResourceKey *key, /* Try to find an ongoing transaction that is a equal to the * specified question */ - t = hashmap_get(scope->transactions, key); + t = hashmap_get(scope->transactions_by_key, key); if (!t) return NULL; @@ -642,7 +661,7 @@ DnsTransaction *dns_scope_find_transaction(DnsScope *scope, DnsResourceKey *key, * data instead of a real packet, if that's requested. */ if (!cache_ok && IN_SET(t->state, DNS_TRANSACTION_SUCCESS, DNS_TRANSACTION_FAILURE) && - !t->received) + t->answer_source != DNS_TRANSACTION_NETWORK) return NULL; return t; @@ -846,3 +865,45 @@ void dns_scope_dump(DnsScope *s, FILE *f) { dns_cache_dump(&s->cache, f); } } + +DnsSearchDomain *dns_scope_get_search_domains(DnsScope *s) { + assert(s); + + /* Returns the list of *local* search domains -- not the + * global ones. */ + + if (s->protocol != DNS_PROTOCOL_DNS) + return NULL; + + if (s->link) + return s->link->search_domains; + + return NULL; +} + +bool dns_scope_has_search_domains(DnsScope *s) { + assert(s); + + /* Tests if there are *any* search domains suitable for this + * scope. This means either local or global ones */ + + if (s->protocol != DNS_PROTOCOL_DNS) + return false; + + if (s->manager->search_domains) + return true; + + if (s->link && s->link->search_domains) + return true; + + return false; +} + +bool dns_scope_name_needs_search_domain(DnsScope *s, const char *name) { + assert(s); + + if (s->protocol != DNS_PROTOCOL_DNS) + return false; + + return dns_name_is_single_label(name); +} diff --git a/src/resolve/resolved-dns-scope.h b/src/resolve/resolved-dns-scope.h index b75f212897..7876410b7d 100644 --- a/src/resolve/resolved-dns-scope.h +++ b/src/resolve/resolved-dns-scope.h @@ -25,9 +25,9 @@ typedef struct DnsScope DnsScope; -#include "resolved-dns-server.h" -#include "resolved-dns-packet.h" #include "resolved-dns-cache.h" +#include "resolved-dns-packet.h" +#include "resolved-dns-server.h" #include "resolved-dns-zone.h" #include "resolved-link.h" @@ -47,8 +47,6 @@ struct DnsScope { Link *link; - char **domains; - DnsCache cache; DnsZone zone; @@ -60,7 +58,18 @@ struct DnsScope { usec_t resend_timeout; usec_t max_rtt; - Hashmap *transactions; + LIST_HEAD(DnsQueryCandidate, query_candidates); + + /* Note that we keep track of ongoing transactions in two + * ways: once in a hashmap, indexed by the rr key, and once in + * a linked list. We use the hashmap to quickly find + * transactions we can reuse for a key. But note that there + * might be multiple transactions for the same key (because + * the zone probing can't reuse a transaction answered from + * the zone or the cache), and the hashmap only tracks the + * most recent entry. */ + Hashmap *transactions_by_key; + LIST_HEAD(DnsTransaction, transactions); LIST_FIELDS(DnsScope, scopes); }; @@ -91,3 +100,8 @@ int dns_scope_notify_conflict(DnsScope *scope, DnsResourceRecord *rr); void dns_scope_check_conflicts(DnsScope *scope, DnsPacket *p); void dns_scope_dump(DnsScope *s, FILE *f); + +DnsSearchDomain *dns_scope_get_search_domains(DnsScope *s); +bool dns_scope_has_search_domains(DnsScope *s); + +bool dns_scope_name_needs_search_domain(DnsScope *s, const char *name); diff --git a/src/resolve/resolved-dns-search-domain.c b/src/resolve/resolved-dns-search-domain.c new file mode 100644 index 0000000000..f9d966abb1 --- /dev/null +++ b/src/resolve/resolved-dns-search-domain.c @@ -0,0 +1,232 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2015 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 "alloc-util.h" +#include "dns-domain.h" +#include "resolved-dns-search-domain.h" + +int dns_search_domain_new( + Manager *m, + DnsSearchDomain **ret, + DnsSearchDomainType type, + Link *l, + const char *name) { + + _cleanup_free_ char *normalized = NULL; + DnsSearchDomain *d; + int r; + + assert(m); + assert((type == DNS_SEARCH_DOMAIN_LINK) == !!l); + assert(name); + + r = dns_name_normalize(name, &normalized); + if (r < 0) + return r; + + if (dns_name_is_root(normalized)) + return -EINVAL; + + if (l) { + if (l->n_search_domains >= LINK_SEARCH_DOMAINS_MAX) + return -E2BIG; + } else { + if (m->n_search_domains >= MANAGER_SEARCH_DOMAINS_MAX) + return -E2BIG; + } + + d = new0(DnsSearchDomain, 1); + if (!d) + return -ENOMEM; + + d->n_ref = 1; + d->manager = m; + d->type = type; + d->name = normalized; + normalized = NULL; + + switch (type) { + + case DNS_SEARCH_DOMAIN_LINK: + d->link = l; + LIST_APPEND(domains, l->search_domains, d); + l->n_search_domains++; + break; + + case DNS_SERVER_SYSTEM: + LIST_APPEND(domains, m->search_domains, d); + m->n_search_domains++; + break; + + default: + assert_not_reached("Unknown search domain type"); + } + + d->linked = true; + + if (ret) + *ret = d; + + return 0; +} + +DnsSearchDomain* dns_search_domain_ref(DnsSearchDomain *d) { + if (!d) + return NULL; + + assert(d->n_ref > 0); + d->n_ref++; + + return d; +} + +DnsSearchDomain* dns_search_domain_unref(DnsSearchDomain *d) { + if (!d) + return NULL; + + assert(d->n_ref > 0); + d->n_ref--; + + if (d->n_ref > 0) + return NULL; + + free(d->name); + free(d); + + return NULL; +} + +void dns_search_domain_unlink(DnsSearchDomain *d) { + assert(d); + assert(d->manager); + + if (!d->linked) + return; + + switch (d->type) { + + case DNS_SEARCH_DOMAIN_LINK: + assert(d->link); + assert(d->link->n_search_domains > 0); + LIST_REMOVE(domains, d->link->search_domains, d); + d->link->n_search_domains--; + break; + + case DNS_SEARCH_DOMAIN_SYSTEM: + assert(d->manager->n_search_domains > 0); + LIST_REMOVE(domains, d->manager->search_domains, d); + d->manager->n_search_domains--; + break; + } + + d->linked = false; + + dns_search_domain_unref(d); +} + +void dns_search_domain_move_back_and_unmark(DnsSearchDomain *d) { + DnsSearchDomain *tail; + + assert(d); + + if (!d->marked) + return; + + d->marked = false; + + if (!d->linked || !d->domains_next) + return; + + switch (d->type) { + + case DNS_SEARCH_DOMAIN_LINK: + assert(d->link); + LIST_FIND_TAIL(domains, d, tail); + LIST_REMOVE(domains, d->link->search_domains, d); + LIST_INSERT_AFTER(domains, d->link->search_domains, tail, d); + break; + + case DNS_SEARCH_DOMAIN_SYSTEM: + LIST_FIND_TAIL(domains, d, tail); + LIST_REMOVE(domains, d->manager->search_domains, d); + LIST_INSERT_AFTER(domains, d->manager->search_domains, tail, d); + break; + + default: + assert_not_reached("Unknown search domain type"); + } +} + +void dns_search_domain_unlink_all(DnsSearchDomain *first) { + DnsSearchDomain *next; + + if (!first) + return; + + next = first->domains_next; + dns_search_domain_unlink(first); + + dns_search_domain_unlink_all(next); +} + +void dns_search_domain_unlink_marked(DnsSearchDomain *first) { + DnsSearchDomain *next; + + if (!first) + return; + + next = first->domains_next; + + if (first->marked) + dns_search_domain_unlink(first); + + dns_search_domain_unlink_marked(next); +} + +void dns_search_domain_mark_all(DnsSearchDomain *first) { + if (!first) + return; + + first->marked = true; + dns_search_domain_mark_all(first->domains_next); +} + +int dns_search_domain_find(DnsSearchDomain *first, const char *name, DnsSearchDomain **ret) { + DnsSearchDomain *d; + int r; + + assert(name); + assert(ret); + + LIST_FOREACH(domains, d, first) { + + r = dns_name_equal(name, d->name); + if (r < 0) + return r; + if (r > 0) { + *ret = d; + return 1; + } + } + + *ret = NULL; + return 0; +} diff --git a/src/resolve/resolved-dns-search-domain.h b/src/resolve/resolved-dns-search-domain.h new file mode 100644 index 0000000000..2e0af31dda --- /dev/null +++ b/src/resolve/resolved-dns-search-domain.h @@ -0,0 +1,75 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#pragma once + +/*** + This file is part of systemd. + + Copyright 2015 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 "macro.h" + +typedef struct DnsSearchDomain DnsSearchDomain; + +typedef enum DnsSearchDomainType { + DNS_SEARCH_DOMAIN_SYSTEM, + DNS_SEARCH_DOMAIN_LINK, +} DnsSearchDomainType; + +#include "resolved-link.h" +#include "resolved-manager.h" + +struct DnsSearchDomain { + Manager *manager; + + unsigned n_ref; + + DnsSearchDomainType type; + Link *link; + + char *name; + + bool marked:1; + + bool linked:1; + LIST_FIELDS(DnsSearchDomain, domains); +}; + +int dns_search_domain_new( + Manager *m, + DnsSearchDomain **ret, + DnsSearchDomainType type, + Link *link, + const char *name); + +DnsSearchDomain* dns_search_domain_ref(DnsSearchDomain *d); +DnsSearchDomain* dns_search_domain_unref(DnsSearchDomain *d); + +void dns_search_domain_unlink(DnsSearchDomain *d); +void dns_search_domain_move_back_and_unmark(DnsSearchDomain *d); + +void dns_search_domain_unlink_all(DnsSearchDomain *first); +void dns_search_domain_unlink_marked(DnsSearchDomain *first); +void dns_search_domain_mark_all(DnsSearchDomain *first); + +int dns_search_domain_find(DnsSearchDomain *first, const char *name, DnsSearchDomain **ret); + +static inline const char* DNS_SEARCH_DOMAIN_NAME(DnsSearchDomain *d) { + return d ? d->name : NULL; +} + +DEFINE_TRIVIAL_CLEANUP_FUNC(DnsSearchDomain*, dns_search_domain_unref); diff --git a/src/resolve/resolved-dns-server.c b/src/resolve/resolved-dns-server.c index e803f635ab..0ebd22fe22 100644 --- a/src/resolve/resolved-dns-server.c +++ b/src/resolve/resolved-dns-server.c @@ -21,7 +21,9 @@ #include "alloc-util.h" #include "resolved-dns-server.h" +#include "resolved-resolv-conf.h" #include "siphash24.h" +#include "string-util.h" /* After how much time to repeat classic DNS requests */ #define DNS_TIMEOUT_MIN_USEC (500 * USEC_PER_MSEC) @@ -35,36 +37,57 @@ int dns_server_new( int family, const union in_addr_union *in_addr) { - DnsServer *s, *tail; + DnsServer *s; assert(m); assert((type == DNS_SERVER_LINK) == !!l); assert(in_addr); + if (!IN_SET(family, AF_INET, AF_INET6)) + return -EAFNOSUPPORT; + + if (l) { + if (l->n_dns_servers >= LINK_DNS_SERVERS_MAX) + return -E2BIG; + } else { + if (m->n_dns_servers >= MANAGER_DNS_SERVERS_MAX) + return -E2BIG; + } + s = new0(DnsServer, 1); if (!s) return -ENOMEM; s->n_ref = 1; + s->manager = m; s->type = type; s->family = family; s->address = *in_addr; s->resend_timeout = DNS_TIMEOUT_MIN_USEC; - if (type == DNS_SERVER_LINK) { - LIST_FIND_TAIL(servers, l->dns_servers, tail); - LIST_INSERT_AFTER(servers, l->dns_servers, tail, s); + switch (type) { + + case DNS_SERVER_LINK: s->link = l; - } else if (type == DNS_SERVER_SYSTEM) { - LIST_FIND_TAIL(servers, m->dns_servers, tail); - LIST_INSERT_AFTER(servers, m->dns_servers, tail, s); - } else if (type == DNS_SERVER_FALLBACK) { - LIST_FIND_TAIL(servers, m->fallback_dns_servers, tail); - LIST_INSERT_AFTER(servers, m->fallback_dns_servers, tail, s); - } else + LIST_APPEND(servers, l->dns_servers, s); + l->n_dns_servers++; + break; + + case DNS_SERVER_SYSTEM: + LIST_APPEND(servers, m->dns_servers, s); + m->n_dns_servers++; + break; + + case DNS_SERVER_FALLBACK: + LIST_APPEND(servers, m->fallback_dns_servers, s); + m->n_dns_servers++; + break; + + default: assert_not_reached("Unknown server type"); + } - s->manager = m; + s->linked = true; /* A new DNS server that isn't fallback is added and the one * we used so far was a fallback one? Then let's try to pick @@ -85,56 +108,127 @@ DnsServer* dns_server_ref(DnsServer *s) { return NULL; assert(s->n_ref > 0); - s->n_ref ++; return s; } -static DnsServer* dns_server_free(DnsServer *s) { +DnsServer* dns_server_unref(DnsServer *s) { if (!s) return NULL; + assert(s->n_ref > 0); + s->n_ref --; + + if (s->n_ref > 0) + return NULL; + + free(s); + return NULL; +} + +void dns_server_unlink(DnsServer *s) { + assert(s); + assert(s->manager); + + /* This removes the specified server from the linked list of + * servers, but any server might still stay around if it has + * refs, for example from an ongoing transaction. */ + + if (!s->linked) + return; + + switch (s->type) { + + case DNS_SERVER_LINK: + assert(s->link); + assert(s->link->n_dns_servers > 0); + LIST_REMOVE(servers, s->link->dns_servers, s); + break; + + case DNS_SERVER_SYSTEM: + assert(s->manager->n_dns_servers > 0); + LIST_REMOVE(servers, s->manager->dns_servers, s); + s->manager->n_dns_servers--; + break; + + case DNS_SERVER_FALLBACK: + assert(s->manager->n_dns_servers > 0); + LIST_REMOVE(servers, s->manager->fallback_dns_servers, s); + s->manager->n_dns_servers--; + break; + } + + s->linked = false; + if (s->link && s->link->current_dns_server == s) link_set_dns_server(s->link, NULL); - if (s->manager && s->manager->current_dns_server == s) + if (s->manager->current_dns_server == s) manager_set_dns_server(s->manager, NULL); - free(s); - - return NULL; + dns_server_unref(s); } -DnsServer* dns_server_unref(DnsServer *s) { - if (!s) - return NULL; +void dns_server_move_back_and_unmark(DnsServer *s) { + DnsServer *tail; - assert(s->n_ref > 0); + assert(s); - if (s->n_ref == 1) - dns_server_free(s); - else - s->n_ref --; + if (!s->marked) + return; - return NULL; + s->marked = false; + + if (!s->linked || !s->servers_next) + return; + + /* Move us to the end of the list, so that the order is + * strictly kept, if we are not at the end anyway. */ + + switch (s->type) { + + case DNS_SERVER_LINK: + assert(s->link); + LIST_FIND_TAIL(servers, s, tail); + LIST_REMOVE(servers, s->link->dns_servers, s); + LIST_INSERT_AFTER(servers, s->link->dns_servers, tail, s); + break; + + case DNS_SERVER_SYSTEM: + LIST_FIND_TAIL(servers, s, tail); + LIST_REMOVE(servers, s->manager->dns_servers, s); + LIST_INSERT_AFTER(servers, s->manager->dns_servers, tail, s); + break; + + case DNS_SERVER_FALLBACK: + LIST_FIND_TAIL(servers, s, tail); + LIST_REMOVE(servers, s->manager->fallback_dns_servers, s); + LIST_INSERT_AFTER(servers, s->manager->fallback_dns_servers, tail, s); + break; + + default: + assert_not_reached("Unknown server type"); + } } void dns_server_packet_received(DnsServer *s, usec_t rtt) { assert(s); - if (rtt > s->max_rtt) { - s->max_rtt = rtt; - s->resend_timeout = MIN(MAX(DNS_TIMEOUT_MIN_USEC, s->max_rtt * 2), - DNS_TIMEOUT_MAX_USEC); - } + if (rtt <= s->max_rtt) + return; + + s->max_rtt = rtt; + s->resend_timeout = MIN(MAX(DNS_TIMEOUT_MIN_USEC, s->max_rtt * 2), DNS_TIMEOUT_MAX_USEC); } void dns_server_packet_lost(DnsServer *s, usec_t usec) { assert(s); - if (s->resend_timeout <= usec) - s->resend_timeout = MIN(s->resend_timeout * 2, DNS_TIMEOUT_MAX_USEC); + if (s->resend_timeout > usec) + return; + + s->resend_timeout = MIN(s->resend_timeout * 2, DNS_TIMEOUT_MAX_USEC); } static void dns_server_hash_func(const void *p, struct siphash *state) { @@ -161,3 +255,140 @@ const struct hash_ops dns_server_hash_ops = { .hash = dns_server_hash_func, .compare = dns_server_compare_func }; + +void dns_server_unlink_all(DnsServer *first) { + DnsServer *next; + + if (!first) + return; + + next = first->servers_next; + dns_server_unlink(first); + + dns_server_unlink_all(next); +} + +void dns_server_unlink_marked(DnsServer *first) { + DnsServer *next; + + if (!first) + return; + + next = first->servers_next; + + if (first->marked) + dns_server_unlink(first); + + dns_server_unlink_marked(next); +} + +void dns_server_mark_all(DnsServer *first) { + if (!first) + return; + + first->marked = true; + dns_server_mark_all(first->servers_next); +} + +DnsServer *dns_server_find(DnsServer *first, int family, const union in_addr_union *in_addr) { + DnsServer *s; + + LIST_FOREACH(servers, s, first) + if (s->family == family && in_addr_equal(family, &s->address, in_addr) > 0) + return s; + + return NULL; +} + +DnsServer *manager_get_first_dns_server(Manager *m, DnsServerType t) { + assert(m); + + switch (t) { + + case DNS_SERVER_SYSTEM: + return m->dns_servers; + + case DNS_SERVER_FALLBACK: + return m->fallback_dns_servers; + + default: + return NULL; + } +} + +DnsServer *manager_set_dns_server(Manager *m, DnsServer *s) { + assert(m); + + if (m->current_dns_server == s) + return s; + + if (s) { + _cleanup_free_ char *ip = NULL; + + in_addr_to_string(s->family, &s->address, &ip); + log_info("Switching to system DNS server %s.", strna(ip)); + } + + dns_server_unref(m->current_dns_server); + m->current_dns_server = dns_server_ref(s); + + if (m->unicast_scope) + dns_cache_flush(&m->unicast_scope->cache); + + return s; +} + +DnsServer *manager_get_dns_server(Manager *m) { + Link *l; + assert(m); + + /* Try to read updates resolv.conf */ + manager_read_resolv_conf(m); + + /* If no DNS server was chose so far, pick the first one */ + if (!m->current_dns_server) + manager_set_dns_server(m, m->dns_servers); + + if (!m->current_dns_server) { + bool found = false; + Iterator i; + + /* No DNS servers configured, let's see if there are + * any on any links. If not, we use the fallback + * servers */ + + HASHMAP_FOREACH(l, m->links, i) + if (l->dns_servers) { + found = true; + break; + } + + if (!found) + manager_set_dns_server(m, m->fallback_dns_servers); + } + + return m->current_dns_server; +} + +void manager_next_dns_server(Manager *m) { + assert(m); + + /* If there's currently no DNS server set, then the next + * manager_get_dns_server() will find one */ + if (!m->current_dns_server) + return; + + /* Change to the next one, but make sure to follow the linked + * list only if the server is still linked. */ + if (m->current_dns_server->linked && m->current_dns_server->servers_next) { + manager_set_dns_server(m, m->current_dns_server->servers_next); + return; + } + + /* If there was no next one, then start from the beginning of + * the list */ + if (m->current_dns_server->type == DNS_SERVER_FALLBACK) + manager_set_dns_server(m, m->fallback_dns_servers); + else + manager_set_dns_server(m, m->dns_servers); +} diff --git a/src/resolve/resolved-dns-server.h b/src/resolve/resolved-dns-server.h index 10111fd6bd..3a78d4a3b5 100644 --- a/src/resolve/resolved-dns-server.h +++ b/src/resolve/resolved-dns-server.h @@ -24,7 +24,6 @@ #include "in-addr-util.h" typedef struct DnsServer DnsServer; -typedef enum DnsServerSource DnsServerSource; typedef enum DnsServerType { DNS_SERVER_SYSTEM, @@ -32,6 +31,7 @@ typedef enum DnsServerType { DNS_SERVER_LINK, } DnsServerType; +#include "resolved-manager.h" #include "resolved-link.h" struct DnsServer { @@ -40,7 +40,6 @@ struct DnsServer { unsigned n_ref; DnsServerType type; - Link *link; int family; @@ -51,23 +50,40 @@ struct DnsServer { bool marked:1; + /* If linked is set, then this server appears in the servers linked list */ + bool linked:1; LIST_FIELDS(DnsServer, servers); }; int dns_server_new( Manager *m, - DnsServer **s, + DnsServer **ret, DnsServerType type, - Link *l, + Link *link, int family, const union in_addr_union *address); DnsServer* dns_server_ref(DnsServer *s); DnsServer* dns_server_unref(DnsServer *s); +void dns_server_unlink(DnsServer *s); +void dns_server_move_back_and_unmark(DnsServer *s); + void dns_server_packet_received(DnsServer *s, usec_t rtt); void dns_server_packet_lost(DnsServer *s, usec_t usec); +DnsServer *dns_server_find(DnsServer *first, int family, const union in_addr_union *in_addr); + +void dns_server_unlink_all(DnsServer *first); +void dns_server_unlink_marked(DnsServer *first); +void dns_server_mark_all(DnsServer *first); + +DnsServer *manager_get_first_dns_server(Manager *m, DnsServerType t); + +DnsServer *manager_set_dns_server(Manager *m, DnsServer *s); +DnsServer *manager_get_dns_server(Manager *m); +void manager_next_dns_server(Manager *m); + DEFINE_TRIVIAL_CLEANUP_FUNC(DnsServer*, dns_server_unref); extern const struct hash_ops dns_server_hash_ops; diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c index 6545f6cd8a..8c4f23a4da 100644 --- a/src/resolve/resolved-dns-transaction.c +++ b/src/resolve/resolved-dns-transaction.c @@ -29,7 +29,7 @@ #include "string-table.h" DnsTransaction* dns_transaction_free(DnsTransaction *t) { - DnsQuery *q; + DnsQueryCandidate *c; DnsZoneItem *i; if (!t) @@ -39,7 +39,8 @@ DnsTransaction* dns_transaction_free(DnsTransaction *t) { dns_packet_unref(t->sent); dns_packet_unref(t->received); - dns_answer_unref(t->cached); + + dns_answer_unref(t->answer); sd_event_source_unref(t->dns_udp_event_source); safe_close(t->dns_udp_fd); @@ -48,7 +49,8 @@ DnsTransaction* dns_transaction_free(DnsTransaction *t) { dns_stream_free(t->stream); if (t->scope) { - hashmap_remove(t->scope->transactions, t->key); + hashmap_remove_value(t->scope->transactions_by_key, t->key, t); + LIST_REMOVE(transactions_by_scope, t->scope->transactions, t); if (t->id != 0) hashmap_remove(t->scope->manager->dns_transactions, UINT_TO_PTR(t->id)); @@ -56,9 +58,10 @@ DnsTransaction* dns_transaction_free(DnsTransaction *t) { dns_resource_key_unref(t->key); - while ((q = set_steal_first(t->queries))) - set_remove(q->transactions, t); - set_free(t->queries); + while ((c = set_steal_first(t->query_candidates))) + set_remove(c->transactions, t); + + set_free(t->query_candidates); while ((i = set_steal_first(t->zone_items))) i->probe_transaction = NULL; @@ -76,7 +79,7 @@ void dns_transaction_gc(DnsTransaction *t) { if (t->block_gc > 0) return; - if (set_isempty(t->queries) && set_isempty(t->zone_items)) + if (set_isempty(t->query_candidates) && set_isempty(t->zone_items)) dns_transaction_free(t); } @@ -92,7 +95,7 @@ int dns_transaction_new(DnsTransaction **ret, DnsScope *s, DnsResourceKey *key) if (r < 0) return r; - r = hashmap_ensure_allocated(&s->transactions, &dns_resource_key_hash_ops); + r = hashmap_ensure_allocated(&s->transactions_by_key, &dns_resource_key_hash_ops); if (r < 0) return r; @@ -101,6 +104,7 @@ int dns_transaction_new(DnsTransaction **ret, DnsScope *s, DnsResourceKey *key) return -ENOMEM; t->dns_udp_fd = -1; + t->answer_source = _DNS_TRANSACTION_SOURCE_INVALID; t->key = dns_resource_key_ref(key); /* Find a fresh, unused transaction id */ @@ -115,12 +119,13 @@ int dns_transaction_new(DnsTransaction **ret, DnsScope *s, DnsResourceKey *key) return r; } - r = hashmap_put(s->transactions, t->key, t); + r = hashmap_replace(s->transactions_by_key, t->key, t); if (r < 0) { hashmap_remove(s->manager->dns_transactions, UINT_TO_PTR(t->id)); return r; } + LIST_PREPEND(transactions_by_scope, s->transactions, t); t->scope = s; if (ret) @@ -136,6 +141,9 @@ static void dns_transaction_stop(DnsTransaction *t) { t->timeout_event_source = sd_event_source_unref(t->timeout_event_source); t->stream = dns_stream_free(t->stream); + + /* Note that we do not drop the UDP socket here, as we want to + * reuse it to repeat the interaction. */ } static void dns_transaction_tentative(DnsTransaction *t, DnsPacket *p) { @@ -181,7 +189,7 @@ static void dns_transaction_tentative(DnsTransaction *t, DnsPacket *p) { } void dns_transaction_complete(DnsTransaction *t, DnsTransactionState state) { - DnsQuery *q; + DnsQueryCandidate *c; DnsZoneItem *z; Iterator i; @@ -192,11 +200,12 @@ void dns_transaction_complete(DnsTransaction *t, DnsTransactionState state) { * should hence not attempt to access the query or transaction * after calling this function. */ - log_debug("Transaction on scope %s on %s/%s now complete with <%s>", + log_debug("Transaction on scope %s on %s/%s now complete with <%s> from %s", dns_protocol_to_string(t->scope->protocol), t->scope->link ? t->scope->link->name : "*", t->scope->family == AF_UNSPEC ? "*" : af_to_name(t->scope->family), - dns_transaction_state_to_string(state)); + dns_transaction_state_to_string(state), + t->answer_source < 0 ? "none" : dns_transaction_source_to_string(t->answer_source)); t->state = state; @@ -205,8 +214,8 @@ void dns_transaction_complete(DnsTransaction *t, DnsTransactionState state) { /* Notify all queries that are interested, but make sure the * transaction isn't freed while we are still looking at it */ t->block_gc++; - SET_FOREACH(q, t->queries, i) - dns_query_ready(q); + SET_FOREACH(c, t->query_candidates, i) + dns_query_candidate_ready(c); SET_FOREACH(z, t->zone_items, i) dns_zone_item_ready(z); t->block_gc--; @@ -314,6 +323,8 @@ static int dns_transaction_open_tcp(DnsTransaction *t) { dns_server_unref(t->server); t->server = dns_server_ref(server); t->received = dns_packet_unref(t->received); + t->answer = dns_answer_unref(t->answer); + t->answer_rcode = 0; t->stream->complete = on_stream_complete; t->stream->transaction = t; @@ -385,6 +396,8 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) { t->received = dns_packet_ref(p); } + t->answer_source = DNS_TRANSACTION_NETWORK; + if (p->ipproto == IPPROTO_TCP) { if (DNS_PACKET_TC(p)) { /* Truncated via TCP? Somebody must be fucking with us */ @@ -453,6 +466,11 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) { return; } + /* Install the answer as answer to the transaction */ + dns_answer_unref(t->answer); + t->answer = dns_answer_ref(p->answer); + t->answer_rcode = DNS_PACKET_RCODE(p); + /* Only consider responses with equivalent query section to the request */ if (p->question->n_keys != 1 || dns_resource_key_equal(p->question->keys[0], t->key) <= 0) { dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY); @@ -460,7 +478,8 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) { } /* According to RFC 4795, section 2.9. only the RRs from the answer section shall be cached */ - dns_cache_put(&t->scope->cache, t->key, DNS_PACKET_RCODE(p), p->answer, DNS_PACKET_ANCOUNT(p), 0, p->family, &p->sender); + if (DNS_PACKET_SHALL_CACHE(p)) + dns_cache_put(&t->scope->cache, t->key, DNS_PACKET_RCODE(p), p->answer, DNS_PACKET_ANCOUNT(p), 0, p->family, &p->sender); if (DNS_PACKET_RCODE(p) == DNS_RCODE_SUCCESS) dns_transaction_complete(t, DNS_TRANSACTION_SUCCESS); @@ -623,8 +642,24 @@ int dns_transaction_go(DnsTransaction *t) { t->n_attempts++; t->start_usec = ts; t->received = dns_packet_unref(t->received); - t->cached = dns_answer_unref(t->cached); - t->cached_rcode = 0; + t->answer = dns_answer_unref(t->answer); + t->answer_rcode = 0; + t->answer_source = _DNS_TRANSACTION_SOURCE_INVALID; + + /* Check the zone, but obly if this transaction is not used + * for probing or verifying a zone item. */ + if (set_isempty(t->zone_items)) { + + r = dns_zone_lookup(&t->scope->zone, t->key, &t->answer, NULL, NULL); + if (r < 0) + return r; + if (r > 0) { + t->answer_rcode = DNS_RCODE_SUCCESS; + t->answer_source = DNS_TRANSACTION_ZONE; + dns_transaction_complete(t, DNS_TRANSACTION_SUCCESS); + return 0; + } + } /* Check the cache, but only if this transaction is not used * for probing or verifying a zone item. */ @@ -638,11 +673,12 @@ int dns_transaction_go(DnsTransaction *t) { /* Let's then prune all outdated entries */ dns_cache_prune(&t->scope->cache); - r = dns_cache_lookup(&t->scope->cache, t->key, &t->cached_rcode, &t->cached); + r = dns_cache_lookup(&t->scope->cache, t->key, &t->answer_rcode, &t->answer); if (r < 0) return r; if (r > 0) { - if (t->cached_rcode == DNS_RCODE_SUCCESS) + t->answer_source = DNS_TRANSACTION_CACHE; + if (t->answer_rcode == DNS_RCODE_SUCCESS) dns_transaction_complete(t, DNS_TRANSACTION_SUCCESS); else dns_transaction_complete(t, DNS_TRANSACTION_FAILURE); @@ -745,3 +781,10 @@ static const char* const dns_transaction_state_table[_DNS_TRANSACTION_STATE_MAX] [DNS_TRANSACTION_ABORTED] = "aborted", }; DEFINE_STRING_TABLE_LOOKUP(dns_transaction_state, DnsTransactionState); + +static const char* const dns_transaction_source_table[_DNS_TRANSACTION_SOURCE_MAX] = { + [DNS_TRANSACTION_NETWORK] = "network", + [DNS_TRANSACTION_CACHE] = "cache", + [DNS_TRANSACTION_ZONE] = "zone", +}; +DEFINE_STRING_TABLE_LOOKUP(dns_transaction_source, DnsTransactionSource); diff --git a/src/resolve/resolved-dns-transaction.h b/src/resolve/resolved-dns-transaction.h index acf6a6f651..ee80dcf5a9 100644 --- a/src/resolve/resolved-dns-transaction.h +++ b/src/resolve/resolved-dns-transaction.h @@ -23,6 +23,7 @@ typedef struct DnsTransaction DnsTransaction; typedef enum DnsTransactionState DnsTransactionState; +typedef enum DnsTransactionSource DnsTransactionSource; enum DnsTransactionState { DNS_TRANSACTION_NULL, @@ -39,10 +40,18 @@ enum DnsTransactionState { _DNS_TRANSACTION_STATE_INVALID = -1 }; -#include "resolved-dns-scope.h" +enum DnsTransactionSource { + DNS_TRANSACTION_NETWORK, + DNS_TRANSACTION_CACHE, + DNS_TRANSACTION_ZONE, + _DNS_TRANSACTION_SOURCE_MAX, + _DNS_TRANSACTION_SOURCE_INVALID = -1 +}; + +#include "resolved-dns-answer.h" #include "resolved-dns-packet.h" #include "resolved-dns-question.h" -#include "resolved-dns-answer.h" +#include "resolved-dns-scope.h" struct DnsTransaction { DnsScope *scope; @@ -55,8 +64,10 @@ struct DnsTransaction { bool initial_jitter; DnsPacket *sent, *received; - DnsAnswer *cached; - int cached_rcode; + + DnsAnswer *answer; + int answer_rcode; + DnsTransactionSource answer_source; usec_t start_usec; sd_event_source *timeout_event_source; @@ -71,9 +82,10 @@ struct DnsTransaction { /* TCP connection logic, if we need it */ DnsStream *stream; - /* Queries this transaction is referenced by and that shall be - * notified about this specific transaction completing. */ - Set *queries; + /* Query candidates this transaction is referenced by and that + * shall be notified about this specific transaction + * completing. */ + Set *query_candidates; /* Zone items this transaction is referenced by and that shall * be notified about completion. */ @@ -96,6 +108,9 @@ void dns_transaction_complete(DnsTransaction *t, DnsTransactionState state); const char* dns_transaction_state_to_string(DnsTransactionState p) _const_; DnsTransactionState dns_transaction_state_from_string(const char *s) _pure_; +const char* dns_transaction_source_to_string(DnsTransactionSource p) _const_; +DnsTransactionSource dns_transaction_source_from_string(const char *s) _pure_; + /* LLMNR Jitter interval, see RFC 4795 Section 7 */ #define LLMNR_JITTER_INTERVAL_USEC (100 * USEC_PER_MSEC) @@ -105,4 +120,4 @@ DnsTransactionState dns_transaction_state_from_string(const char *s) _pure_; /* Maximum attempts to send LLMNR requests, see RFC 4795 Section 2.7 */ #define LLMNR_TRANSACTION_ATTEMPTS_MAX 3 -#define TRANSACTION_ATTEMPTS_MAX(p) (p == DNS_PROTOCOL_LLMNR ? LLMNR_TRANSACTION_ATTEMPTS_MAX : DNS_TRANSACTION_ATTEMPTS_MAX) +#define TRANSACTION_ATTEMPTS_MAX(p) ((p) == DNS_PROTOCOL_LLMNR ? LLMNR_TRANSACTION_ATTEMPTS_MAX : DNS_TRANSACTION_ATTEMPTS_MAX) diff --git a/src/resolve/resolved-dns-zone.c b/src/resolve/resolved-dns-zone.c index 48dcf76daa..493d11dd14 100644 --- a/src/resolve/resolved-dns-zone.c +++ b/src/resolve/resolved-dns-zone.c @@ -283,97 +283,76 @@ int dns_zone_put(DnsZone *z, DnsScope *s, DnsResourceRecord *rr, bool probe) { return 0; } -int dns_zone_lookup(DnsZone *z, DnsQuestion *q, DnsAnswer **ret_answer, DnsAnswer **ret_soa, bool *ret_tentative) { +int dns_zone_lookup(DnsZone *z, DnsResourceKey *key, DnsAnswer **ret_answer, DnsAnswer **ret_soa, bool *ret_tentative) { _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL, *soa = NULL; - unsigned i, n_answer = 0, n_soa = 0; - bool tentative = true; + unsigned n_answer = 0; + DnsZoneItem *j, *first; + bool tentative = true, need_soa = false; int r; assert(z); - assert(q); + assert(key); assert(ret_answer); - assert(ret_soa); - if (q->n_keys <= 0) { - *ret_answer = NULL; - *ret_soa = NULL; - - if (ret_tentative) - *ret_tentative = false; + /* First iteration, count what we have */ - return 0; - } + if (key->type == DNS_TYPE_ANY || key->class == DNS_CLASS_ANY) { + bool found = false, added = false; + int k; - /* First iteration, count what we have */ - for (i = 0; i < q->n_keys; i++) { - DnsZoneItem *j, *first; + /* If this is a generic match, then we have to + * go through the list by the name and look + * for everything manually */ - if (q->keys[i]->type == DNS_TYPE_ANY || - q->keys[i]->class == DNS_CLASS_ANY) { - bool found = false, added = false; - int k; + first = hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(key)); + LIST_FOREACH(by_name, j, first) { + if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING)) + continue; - /* If this is a generic match, then we have to - * go through the list by the name and look - * for everything manually */ + found = true; - first = hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(q->keys[i])); - LIST_FOREACH(by_name, j, first) { - if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING)) - continue; + k = dns_resource_key_match_rr(key, j->rr, NULL); + if (k < 0) + return k; + if (k > 0) { + n_answer++; + added = true; + } - found = true; + } - k = dns_resource_key_match_rr(q->keys[i], j->rr); - if (k < 0) - return k; - if (k > 0) { - n_answer++; - added = true; - } + if (found && !added) + need_soa = true; - } + } else { + bool found = false; - if (found && !added) - n_soa++; + /* If this is a specific match, then look for + * the right key immediately */ - } else { - bool found = false; + first = hashmap_get(z->by_key, key); + LIST_FOREACH(by_key, j, first) { + if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING)) + continue; - /* If this is a specific match, then look for - * the right key immediately */ + found = true; + n_answer++; + } - first = hashmap_get(z->by_key, q->keys[i]); - LIST_FOREACH(by_key, j, first) { + if (!found) { + first = hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(key)); + LIST_FOREACH(by_name, j, first) { if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING)) continue; - found = true; - n_answer++; - } - - if (!found) { - first = hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(q->keys[i])); - LIST_FOREACH(by_name, j, first) { - if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING)) - continue; - - n_soa++; - break; - } + need_soa = true; + break; } } } - if (n_answer <= 0 && n_soa <= 0) { - *ret_answer = NULL; - *ret_soa = NULL; - - if (ret_tentative) - *ret_tentative = false; - - return 0; - } + if (n_answer <= 0 && !need_soa) + goto return_empty; if (n_answer > 0) { answer = dns_answer_new(n_answer); @@ -381,99 +360,113 @@ int dns_zone_lookup(DnsZone *z, DnsQuestion *q, DnsAnswer **ret_answer, DnsAnswe return -ENOMEM; } - if (n_soa > 0) { - soa = dns_answer_new(n_soa); + if (need_soa) { + soa = dns_answer_new(1); if (!soa) return -ENOMEM; } /* Second iteration, actually add the RRs to the answers */ - for (i = 0; i < q->n_keys; i++) { - DnsZoneItem *j, *first; - - if (q->keys[i]->type == DNS_TYPE_ANY || - q->keys[i]->class == DNS_CLASS_ANY) { - bool found = false, added = false; - int k; + if (key->type == DNS_TYPE_ANY || key->class == DNS_CLASS_ANY) { + bool found = false, added = false; + int k; - first = hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(q->keys[i])); - LIST_FOREACH(by_name, j, first) { - if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING)) - continue; - - found = true; + first = hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(key)); + LIST_FOREACH(by_name, j, first) { + if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING)) + continue; - if (j->state != DNS_ZONE_ITEM_PROBING) - tentative = false; + found = true; - k = dns_resource_key_match_rr(q->keys[i], j->rr); - if (k < 0) - return k; - if (k > 0) { - r = dns_answer_add(answer, j->rr, 0); - if (r < 0) - return r; + if (j->state != DNS_ZONE_ITEM_PROBING) + tentative = false; - added = true; - } - } - - if (found && !added) { - r = dns_answer_add_soa(soa, DNS_RESOURCE_KEY_NAME(q->keys[i]), LLMNR_DEFAULT_TTL); + k = dns_resource_key_match_rr(key, j->rr, NULL); + if (k < 0) + return k; + if (k > 0) { + r = dns_answer_add(answer, j->rr, 0); if (r < 0) return r; + + added = true; } - } else { - bool found = false; + } - first = hashmap_get(z->by_key, q->keys[i]); - LIST_FOREACH(by_key, j, first) { - if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING)) - continue; + if (found && !added) { + r = dns_answer_add_soa(soa, DNS_RESOURCE_KEY_NAME(key), LLMNR_DEFAULT_TTL); + if (r < 0) + return r; + } + } else { + bool found = false; - found = true; + first = hashmap_get(z->by_key, key); + LIST_FOREACH(by_key, j, first) { + if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING)) + continue; - if (j->state != DNS_ZONE_ITEM_PROBING) - tentative = false; + found = true; - r = dns_answer_add(answer, j->rr, 0); - if (r < 0) - return r; - } + if (j->state != DNS_ZONE_ITEM_PROBING) + tentative = false; + + r = dns_answer_add(answer, j->rr, 0); + if (r < 0) + return r; + } - if (!found) { - bool add_soa = false; + if (!found) { + bool add_soa = false; - first = hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(q->keys[i])); - LIST_FOREACH(by_name, j, first) { - if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING)) - continue; + first = hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(key)); + LIST_FOREACH(by_name, j, first) { + if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING)) + continue; - if (j->state != DNS_ZONE_ITEM_PROBING) - tentative = false; + if (j->state != DNS_ZONE_ITEM_PROBING) + tentative = false; - add_soa = true; - } + add_soa = true; + } - if (add_soa) { - r = dns_answer_add_soa(soa, DNS_RESOURCE_KEY_NAME(q->keys[i]), LLMNR_DEFAULT_TTL); - if (r < 0) - return r; - } + if (add_soa) { + r = dns_answer_add_soa(soa, DNS_RESOURCE_KEY_NAME(key), LLMNR_DEFAULT_TTL); + if (r < 0) + return r; } } } + /* If the caller sets ret_tentative to NULL, then use this as + * indication to not return tentative entries */ + + if (!ret_tentative && tentative) + goto return_empty; + *ret_answer = answer; answer = NULL; - *ret_soa = soa; - soa = NULL; + if (ret_soa) { + *ret_soa = soa; + soa = NULL; + } if (ret_tentative) *ret_tentative = tentative; return 1; + +return_empty: + *ret_answer = NULL; + + if (ret_soa) + *ret_soa = NULL; + + if (ret_tentative) + *ret_tentative = false; + + return 0; } void dns_zone_item_conflict(DnsZoneItem *i) { diff --git a/src/resolve/resolved-dns-zone.h b/src/resolve/resolved-dns-zone.h index 495d17cdb1..44a8624c30 100644 --- a/src/resolve/resolved-dns-zone.h +++ b/src/resolve/resolved-dns-zone.h @@ -31,9 +31,9 @@ typedef struct DnsZone { typedef struct DnsZoneItem DnsZoneItem; typedef enum DnsZoneItemState DnsZoneItemState; -#include "resolved-dns-rr.h" -#include "resolved-dns-question.h" #include "resolved-dns-answer.h" +#include "resolved-dns-question.h" +#include "resolved-dns-rr.h" #include "resolved-dns-transaction.h" /* RFC 4795 Section 2.8. suggests a TTL of 30s by default */ @@ -67,7 +67,7 @@ void dns_zone_flush(DnsZone *z); int dns_zone_put(DnsZone *z, DnsScope *s, DnsResourceRecord *rr, bool probe); void dns_zone_remove_rr(DnsZone *z, DnsResourceRecord *rr); -int dns_zone_lookup(DnsZone *z, DnsQuestion *q, DnsAnswer **answer, DnsAnswer **soa, bool *tentative); +int dns_zone_lookup(DnsZone *z, DnsResourceKey *key, DnsAnswer **answer, DnsAnswer **soa, bool *tentative); void dns_zone_item_conflict(DnsZoneItem *i); void dns_zone_item_ready(DnsZoneItem *i); diff --git a/src/resolve/resolved-gperf.gperf b/src/resolve/resolved-gperf.gperf index 8e78fbf06a..50662656d5 100644 --- a/src/resolve/resolved-gperf.gperf +++ b/src/resolve/resolved-gperf.gperf @@ -14,6 +14,7 @@ struct ConfigPerfItem; %struct-type %includes %% -Resolve.DNS, config_parse_dnsv, DNS_SERVER_SYSTEM, 0 -Resolve.FallbackDNS, config_parse_dnsv, DNS_SERVER_FALLBACK, 0 -Resolve.LLMNR, config_parse_support, 0, offsetof(Manager, llmnr_support) +Resolve.DNS, config_parse_dns_servers, DNS_SERVER_SYSTEM, 0 +Resolve.FallbackDNS, config_parse_dns_servers, DNS_SERVER_FALLBACK, 0 +Resolve.Domains, config_parse_search_domains, 0, 0 +Resolve.LLMNR, config_parse_support, 0, offsetof(Manager, llmnr_support) diff --git a/src/resolve/resolved-link.c b/src/resolve/resolved-link.c index 2892641075..ddd9427dab 100644 --- a/src/resolve/resolved-link.c +++ b/src/resolve/resolved-link.c @@ -65,19 +65,15 @@ Link *link_free(Link *l) { if (!l) return NULL; + dns_server_unlink_marked(l->dns_servers); + dns_search_domain_unlink_all(l->search_domains); + while (l->addresses) link_address_free(l->addresses); if (l->manager) hashmap_remove(l->manager->links, INT_TO_PTR(l->ifindex)); - while (l->dns_servers) { - DnsServer *s = l->dns_servers; - - LIST_REMOVE(servers, l->dns_servers, s); - dns_server_unref(s); - } - dns_scope_free(l->unicast_scope); dns_scope_free(l->llmnr_ipv4_scope); dns_scope_free(l->llmnr_ipv6_scope); @@ -158,7 +154,6 @@ int link_update_rtnl(Link *l, sd_netlink_message *m) { static int link_update_dns_servers(Link *l) { _cleanup_strv_free_ char **nameservers = NULL; char **nameserver; - DnsServer *s, *nx; int r; assert(l); @@ -167,20 +162,20 @@ static int link_update_dns_servers(Link *l) { if (r < 0) goto clear; - LIST_FOREACH(servers, s, l->dns_servers) - s->marked = true; + dns_server_mark_all(l->dns_servers); STRV_FOREACH(nameserver, nameservers) { union in_addr_union a; + DnsServer *s; int family; r = in_addr_from_string_auto(*nameserver, &family, &a); if (r < 0) goto clear; - s = link_find_dns_server(l, family, &a); + s = dns_server_find(l->dns_servers, family, &a); if (s) - s->marked = false; + dns_server_move_back_and_unmark(s); else { r = dns_server_new(l->manager, NULL, DNS_SERVER_LINK, l, family, &a); if (r < 0) @@ -188,22 +183,11 @@ static int link_update_dns_servers(Link *l) { } } - LIST_FOREACH_SAFE(servers, s, nx, l->dns_servers) - if (s->marked) { - LIST_REMOVE(servers, l->dns_servers, s); - dns_server_unref(s); - } - + dns_server_unlink_marked(l->dns_servers); return 0; clear: - while (l->dns_servers) { - s = l->dns_servers; - - LIST_REMOVE(servers, l->dns_servers, s); - dns_server_unref(s); - } - + dns_server_unlink_all(l->dns_servers); return r; } @@ -236,29 +220,56 @@ clear: return r; } -static int link_update_domains(Link *l) { +static int link_update_search_domains(Link *l) { + _cleanup_strv_free_ char **domains = NULL; + char **i; int r; - if (!l->unicast_scope) - return 0; - - l->unicast_scope->domains = strv_free(l->unicast_scope->domains); + assert(l); - r = sd_network_link_get_domains(l->ifindex, - &l->unicast_scope->domains); + r = sd_network_link_get_domains(l->ifindex, &domains); if (r < 0) - return r; + goto clear; + + dns_search_domain_mark_all(l->search_domains); + + STRV_FOREACH(i, domains) { + DnsSearchDomain *d; + + r = dns_search_domain_find(l->search_domains, *i, &d); + if (r < 0) + goto clear; + + if (r > 0) + dns_search_domain_move_back_and_unmark(d); + else { + r = dns_search_domain_new(l->manager, NULL, DNS_SEARCH_DOMAIN_LINK, l, *i); + if (r < 0) + goto clear; + } + } + dns_search_domain_unlink_marked(l->search_domains); return 0; + +clear: + dns_search_domain_unlink_all(l->search_domains); + return r; } int link_update_monitor(Link *l) { + int r; + assert(l); link_update_dns_servers(l); link_update_llmnr_support(l); link_allocate_scopes(l); - link_update_domains(l); + + r = link_update_search_domains(l); + if (r < 0) + log_warning_errno(r, "Failed to read search domains for interface %s, ignoring: %m", l->name); + link_add_rrs(l, false); return 0; @@ -303,17 +314,6 @@ LinkAddress *link_find_address(Link *l, int family, const union in_addr_union *i return NULL; } -DnsServer* link_find_dns_server(Link *l, int family, const union in_addr_union *in_addr) { - DnsServer *s; - - assert(l); - - LIST_FOREACH(servers, s, l->dns_servers) - if (s->family == family && in_addr_equal(family, &s->address, in_addr)) - return s; - return NULL; -} - DnsServer* link_set_dns_server(Link *l, DnsServer *s) { assert(l); @@ -327,7 +327,8 @@ DnsServer* link_set_dns_server(Link *l, DnsServer *s) { log_info("Switching to DNS server %s for interface %s.", strna(ip), l->name); } - l->current_dns_server = s; + dns_server_unref(l->current_dns_server); + l->current_dns_server = dns_server_ref(s); if (l->unicast_scope) dns_cache_flush(&l->unicast_scope->cache); @@ -350,7 +351,9 @@ void link_next_dns_server(Link *l) { if (!l->current_dns_server) return; - if (l->current_dns_server->servers_next) { + /* Change to the next one, but make sure to follow the linked + * list only if this server is actually still linked. */ + if (l->current_dns_server->linked && l->current_dns_server->servers_next) { link_set_dns_server(l, l->current_dns_server->servers_next); return; } diff --git a/src/resolve/resolved-link.h b/src/resolve/resolved-link.h index e3ab27c249..eb00015bd5 100644 --- a/src/resolve/resolved-link.h +++ b/src/resolve/resolved-link.h @@ -30,8 +30,13 @@ typedef struct Link Link; typedef struct LinkAddress LinkAddress; #include "resolved-dns-rr.h" +#include "resolved-dns-search-domain.h" +#include "resolved-dns-server.h" #include "resolved-manager.h" +#define LINK_SEARCH_DOMAINS_MAX 32 +#define LINK_DNS_SERVERS_MAX 32 + struct LinkAddress { Link *link; @@ -56,6 +61,10 @@ struct Link { LIST_HEAD(DnsServer, dns_servers); DnsServer *current_dns_server; + unsigned n_dns_servers; + + LIST_HEAD(DnsSearchDomain, search_domains); + unsigned n_search_domains; Support llmnr_support; @@ -76,7 +85,6 @@ LinkAddress* link_find_address(Link *l, int family, const union in_addr_union *i void link_add_rrs(Link *l, bool force_remove); DnsServer* link_set_dns_server(Link *l, DnsServer *s); -DnsServer* link_find_dns_server(Link *l, int family, const union in_addr_union *in_addr); DnsServer* link_get_dns_server(Link *l); void link_next_dns_server(Link *l); diff --git a/src/resolve/resolved-llmnr.c b/src/resolve/resolved-llmnr.c index 5c3a4a00c3..6a7ff9d245 100644 --- a/src/resolve/resolved-llmnr.c +++ b/src/resolve/resolved-llmnr.c @@ -19,8 +19,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <resolv.h> #include <netinet/in.h> +#include <resolv.h> #include "fd-util.h" #include "resolved-llmnr.h" diff --git a/src/resolve/resolved-manager.c b/src/resolve/resolved-manager.c index a588538b52..f1f454c786 100644 --- a/src/resolve/resolved-manager.c +++ b/src/resolve/resolved-manager.c @@ -21,7 +21,6 @@ #include <netinet/in.h> #include <poll.h> -#include <resolv.h> #include <sys/ioctl.h> #include "af-list.h" @@ -40,6 +39,7 @@ #include "resolved-conf.h" #include "resolved-llmnr.h" #include "resolved-manager.h" +#include "resolved-resolv-conf.h" #include "socket-util.h" #include "string-table.h" #include "string-util.h" @@ -351,7 +351,7 @@ static int determine_hostname(char **llmnr_hostname, char **mdns_hostname) { return -EINVAL; } - r = dns_label_escape(label, r, &n); + r = dns_label_escape_new(label, r, &n); if (r < 0) return log_error_errno(r, "Failed to escape host name: %m"); @@ -476,10 +476,7 @@ int manager_new(Manager **ret) { m->llmnr_support = SUPPORT_YES; m->read_resolv_conf = true; - - r = manager_parse_dns_server(m, DNS_SERVER_FALLBACK, DNS_SERVERS); - if (r < 0) - return r; + m->need_builtin_fallbacks = true; r = sd_event_default(&m->event); if (r < 0) @@ -536,15 +533,16 @@ Manager *manager_free(Manager *m) { if (!m) return NULL; + dns_server_unlink_all(m->dns_servers); + dns_server_unlink_all(m->fallback_dns_servers); + dns_search_domain_unlink_all(m->search_domains); + while ((l = hashmap_first(m->links))) link_free(l); while (m->dns_queries) dns_query_free(m->dns_queries); - manager_flush_dns_servers(m, DNS_SERVER_SYSTEM); - manager_flush_dns_servers(m, DNS_SERVER_FALLBACK); - dns_scope_free(m->unicast_scope); hashmap_free(m->links); @@ -553,6 +551,9 @@ Manager *manager_free(Manager *m) { sd_event_source_unref(m->network_event_source); sd_network_monitor_unref(m->network_monitor); + sd_netlink_unref(m->rtnl); + sd_event_source_unref(m->rtnl_event_source); + manager_llmnr_stop(m); sd_bus_slot_unref(m->prepare_for_sleep_slot); @@ -576,294 +577,6 @@ Manager *manager_free(Manager *m) { return NULL; } -int manager_read_resolv_conf(Manager *m) { - _cleanup_fclose_ FILE *f = NULL; - struct stat st, own; - char line[LINE_MAX]; - DnsServer *s, *nx; - usec_t t; - int r; - - assert(m); - - /* Reads the system /etc/resolv.conf, if it exists and is not - * symlinked to our own resolv.conf instance */ - - if (!m->read_resolv_conf) - return 0; - - r = stat("/etc/resolv.conf", &st); - if (r < 0) { - if (errno != ENOENT) - log_warning_errno(errno, "Failed to open /etc/resolv.conf: %m"); - r = -errno; - goto clear; - } - - /* Have we already seen the file? */ - t = timespec_load(&st.st_mtim); - if (t == m->resolv_conf_mtime) - return 0; - - m->resolv_conf_mtime = t; - - /* Is it symlinked to our own file? */ - if (stat("/run/systemd/resolve/resolv.conf", &own) >= 0 && - st.st_dev == own.st_dev && - st.st_ino == own.st_ino) { - r = 0; - goto clear; - } - - f = fopen("/etc/resolv.conf", "re"); - if (!f) { - if (errno != ENOENT) - log_warning_errno(errno, "Failed to open /etc/resolv.conf: %m"); - r = -errno; - goto clear; - } - - if (fstat(fileno(f), &st) < 0) { - r = log_error_errno(errno, "Failed to stat open file: %m"); - goto clear; - } - - LIST_FOREACH(servers, s, m->dns_servers) - s->marked = true; - - FOREACH_LINE(line, f, r = -errno; goto clear) { - union in_addr_union address; - int family; - char *l; - const char *a; - - truncate_nl(line); - - l = strstrip(line); - if (*l == '#' || *l == ';') - continue; - - a = first_word(l, "nameserver"); - if (!a) - continue; - - r = in_addr_from_string_auto(a, &family, &address); - if (r < 0) { - log_warning("Failed to parse name server %s.", a); - continue; - } - - LIST_FOREACH(servers, s, m->dns_servers) - if (s->family == family && in_addr_equal(family, &s->address, &address) > 0) - break; - - if (s) - s->marked = false; - else { - r = dns_server_new(m, NULL, DNS_SERVER_SYSTEM, NULL, family, &address); - if (r < 0) - goto clear; - } - } - - LIST_FOREACH_SAFE(servers, s, nx, m->dns_servers) - if (s->marked) { - LIST_REMOVE(servers, m->dns_servers, s); - dns_server_unref(s); - } - - /* Whenever /etc/resolv.conf changes, start using the first - * DNS server of it. This is useful to deal with broken - * network managing implementations (like NetworkManager), - * that when connecting to a VPN place both the VPN DNS - * servers and the local ones in /etc/resolv.conf. Without - * resetting the DNS server to use back to the first entry we - * will continue to use the local one thus being unable to - * resolve VPN domains. */ - manager_set_dns_server(m, m->dns_servers); - - return 0; - -clear: - while (m->dns_servers) { - s = m->dns_servers; - - LIST_REMOVE(servers, m->dns_servers, s); - dns_server_unref(s); - } - - return r; -} - -static void write_resolv_conf_server(DnsServer *s, FILE *f, unsigned *count) { - _cleanup_free_ char *t = NULL; - int r; - - assert(s); - assert(f); - assert(count); - - r = in_addr_to_string(s->family, &s->address, &t); - if (r < 0) { - log_warning_errno(r, "Invalid DNS address. Ignoring: %m"); - return; - } - - if (*count == MAXNS) - fputs("# Too many DNS servers configured, the following entries may be ignored.\n", f); - - fprintf(f, "nameserver %s\n", t); - (*count) ++; -} - -static void write_resolv_conf_search( - const char *domain, FILE *f, - unsigned *count, - unsigned *length) { - - assert(domain); - assert(f); - assert(length); - - if (*count >= MAXDNSRCH || - *length + strlen(domain) > 256) { - if (*count == MAXDNSRCH) - fputs(" # Too many search domains configured, remaining ones ignored.", f); - if (*length <= 256) - fputs(" # Total length of all search domains is too long, remaining ones ignored.", f); - - return; - } - - fprintf(f, " %s", domain); - - (*length) += strlen(domain); - (*count) ++; -} - -static int write_resolv_conf_contents(FILE *f, OrderedSet *dns, OrderedSet *domains) { - Iterator i; - - fputs("# This file is managed by systemd-resolved(8). Do not edit.\n#\n" - "# Third party programs must not access this file directly, but\n" - "# only through the symlink at /etc/resolv.conf. To manage\n" - "# resolv.conf(5) in a different way, replace the symlink by a\n" - "# static file or a different symlink.\n\n", f); - - if (ordered_set_isempty(dns)) - fputs("# No DNS servers known.\n", f); - else { - DnsServer *s; - unsigned count = 0; - - ORDERED_SET_FOREACH(s, dns, i) - write_resolv_conf_server(s, f, &count); - } - - if (!ordered_set_isempty(domains)) { - unsigned length = 0, count = 0; - char *domain; - - fputs("search", f); - ORDERED_SET_FOREACH(domain, domains, i) - write_resolv_conf_search(domain, f, &count, &length); - fputs("\n", f); - } - - return fflush_and_check(f); -} - -int manager_write_resolv_conf(Manager *m) { - static const char path[] = "/run/systemd/resolve/resolv.conf"; - _cleanup_free_ char *temp_path = NULL; - _cleanup_fclose_ FILE *f = NULL; - _cleanup_ordered_set_free_ OrderedSet *dns = NULL, *domains = NULL; - DnsServer *s; - Iterator i; - Link *l; - int r; - - assert(m); - - /* Read the system /etc/resolv.conf first */ - manager_read_resolv_conf(m); - - /* Add the full list to a set, to filter out duplicates */ - dns = ordered_set_new(&dns_server_hash_ops); - if (!dns) - return -ENOMEM; - - domains = ordered_set_new(&dns_name_hash_ops); - if (!domains) - return -ENOMEM; - - /* First add the system-wide servers */ - LIST_FOREACH(servers, s, m->dns_servers) { - r = ordered_set_put(dns, s); - if (r == -EEXIST) - continue; - if (r < 0) - return r; - } - - /* Then, add the per-link servers and domains */ - HASHMAP_FOREACH(l, m->links, i) { - char **domain; - - LIST_FOREACH(servers, s, l->dns_servers) { - r = ordered_set_put(dns, s); - if (r == -EEXIST) - continue; - if (r < 0) - return r; - } - - if (!l->unicast_scope) - continue; - - STRV_FOREACH(domain, l->unicast_scope->domains) { - r = ordered_set_put(domains, *domain); - if (r == -EEXIST) - continue; - if (r < 0) - return r; - } - } - - /* If we found nothing, add the fallback servers */ - if (ordered_set_isempty(dns)) { - LIST_FOREACH(servers, s, m->fallback_dns_servers) { - r = ordered_set_put(dns, s); - if (r == -EEXIST) - continue; - if (r < 0) - return r; - } - } - - r = fopen_temporary_label(path, path, &f, &temp_path); - if (r < 0) - return r; - - fchmod(fileno(f), 0644); - - r = write_resolv_conf_contents(f, dns, domains); - if (r < 0) - goto fail; - - if (rename(temp_path, path) < 0) { - r = -errno; - goto fail; - } - - return 0; - -fail: - (void) unlink(path); - (void) unlink(temp_path); - return r; -} - int manager_recv(Manager *m, int fd, DnsProtocol protocol, DnsPacket **ret) { _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; union { @@ -1171,97 +884,6 @@ int manager_send(Manager *m, int fd, int ifindex, int family, const union in_add return -EAFNOSUPPORT; } -DnsServer* manager_find_dns_server(Manager *m, int family, const union in_addr_union *in_addr) { - DnsServer *s; - - assert(m); - assert(in_addr); - - LIST_FOREACH(servers, s, m->dns_servers) - if (s->family == family && in_addr_equal(family, &s->address, in_addr) > 0) - return s; - - LIST_FOREACH(servers, s, m->fallback_dns_servers) - if (s->family == family && in_addr_equal(family, &s->address, in_addr) > 0) - return s; - - return NULL; -} - -DnsServer *manager_set_dns_server(Manager *m, DnsServer *s) { - assert(m); - - if (m->current_dns_server == s) - return s; - - if (s) { - _cleanup_free_ char *ip = NULL; - - in_addr_to_string(s->family, &s->address, &ip); - log_info("Switching to system DNS server %s.", strna(ip)); - } - - m->current_dns_server = s; - - if (m->unicast_scope) - dns_cache_flush(&m->unicast_scope->cache); - - return s; -} - -DnsServer *manager_get_dns_server(Manager *m) { - Link *l; - assert(m); - - /* Try to read updates resolv.conf */ - manager_read_resolv_conf(m); - - if (!m->current_dns_server) - manager_set_dns_server(m, m->dns_servers); - - if (!m->current_dns_server) { - bool found = false; - Iterator i; - - /* No DNS servers configured, let's see if there are - * any on any links. If not, we use the fallback - * servers */ - - HASHMAP_FOREACH(l, m->links, i) - if (l->dns_servers) { - found = true; - break; - } - - if (!found) - manager_set_dns_server(m, m->fallback_dns_servers); - } - - return m->current_dns_server; -} - -void manager_next_dns_server(Manager *m) { - assert(m); - - /* If there's currently no DNS server set, then the next - * manager_get_dns_server() will find one */ - if (!m->current_dns_server) - return; - - /* Change to the next one */ - if (m->current_dns_server->servers_next) { - manager_set_dns_server(m, m->current_dns_server->servers_next); - return; - } - - /* If there was no next one, then start from the beginning of - * the list */ - if (m->current_dns_server->type == DNS_SERVER_FALLBACK) - manager_set_dns_server(m, m->fallback_dns_servers); - else - manager_set_dns_server(m, m->dns_servers); -} - uint32_t manager_find_mtu(Manager *m) { uint32_t mtu = 0; Link *l; @@ -1415,42 +1037,102 @@ void manager_verify_all(Manager *m) { dns_zone_verify_all(&s->zone); } -void manager_flush_dns_servers(Manager *m, DnsServerType t) { +int manager_is_own_hostname(Manager *m, const char *name) { + int r; + + assert(m); + assert(name); + + if (m->llmnr_hostname) { + r = dns_name_equal(name, m->llmnr_hostname); + if (r != 0) + return r; + } + + if (m->mdns_hostname) + return dns_name_equal(name, m->mdns_hostname); + + return 0; +} + +int manager_compile_dns_servers(Manager *m, OrderedSet **dns) { DnsServer *s; + Iterator i; + Link *l; + int r; assert(m); + assert(dns); + + r = ordered_set_ensure_allocated(dns, &dns_server_hash_ops); + if (r < 0) + return r; - if (t == DNS_SERVER_SYSTEM) - while (m->dns_servers) { - s = m->dns_servers; + /* First add the system-wide servers and domains */ + LIST_FOREACH(servers, s, m->dns_servers) { + r = ordered_set_put(*dns, s); + if (r == -EEXIST) + continue; + if (r < 0) + return r; + } - LIST_REMOVE(servers, m->dns_servers, s); - dns_server_unref(s); + /* Then, add the per-link servers */ + HASHMAP_FOREACH(l, m->links, i) { + LIST_FOREACH(servers, s, l->dns_servers) { + r = ordered_set_put(*dns, s); + if (r == -EEXIST) + continue; + if (r < 0) + return r; } + } - if (t == DNS_SERVER_FALLBACK) - while (m->fallback_dns_servers) { - s = m->fallback_dns_servers; - - LIST_REMOVE(servers, m->fallback_dns_servers, s); - dns_server_unref(s); + /* If we found nothing, add the fallback servers */ + if (ordered_set_isempty(*dns)) { + LIST_FOREACH(servers, s, m->fallback_dns_servers) { + r = ordered_set_put(*dns, s); + if (r == -EEXIST) + continue; + if (r < 0) + return r; } + } + + return 0; } -int manager_is_own_hostname(Manager *m, const char *name) { +int manager_compile_search_domains(Manager *m, OrderedSet **domains) { + DnsSearchDomain *d; + Iterator i; + Link *l; int r; assert(m); - assert(name); + assert(domains); - if (m->llmnr_hostname) { - r = dns_name_equal(name, m->llmnr_hostname); - if (r != 0) + r = ordered_set_ensure_allocated(domains, &dns_name_hash_ops); + if (r < 0) + return r; + + LIST_FOREACH(domains, d, m->search_domains) { + r = ordered_set_put(*domains, d->name); + if (r == -EEXIST) + continue; + if (r < 0) return r; } - if (m->mdns_hostname) - return dns_name_equal(name, m->mdns_hostname); + HASHMAP_FOREACH(l, m->links, i) { + + LIST_FOREACH(domains, d, l->search_domains) { + r = ordered_set_put(*domains, d->name); + if (r == -EEXIST) + continue; + if (r < 0) + return r; + } + } return 0; } diff --git a/src/resolve/resolved-manager.h b/src/resolve/resolved-manager.h index fe7fe99505..d00c444583 100644 --- a/src/resolve/resolved-manager.h +++ b/src/resolve/resolved-manager.h @@ -22,10 +22,12 @@ ***/ #include "sd-event.h" -#include "sd-network.h" #include "sd-netlink.h" -#include "list.h" +#include "sd-network.h" + #include "hashmap.h" +#include "list.h" +#include "ordered-set.h" typedef struct Manager Manager; typedef enum Support Support; @@ -39,9 +41,14 @@ enum Support { }; #include "resolved-dns-query.h" +#include "resolved-dns-search-domain.h" +#include "resolved-dns-server.h" #include "resolved-dns-stream.h" #include "resolved-link.h" +#define MANAGER_SEARCH_DOMAINS_MAX 32 +#define MANAGER_DNS_SERVERS_MAX 32 + struct Manager { sd_event *event; @@ -67,9 +74,15 @@ struct Manager { /* Unicast dns */ LIST_HEAD(DnsServer, dns_servers); LIST_HEAD(DnsServer, fallback_dns_servers); + unsigned n_dns_servers; /* counts both main and fallback */ DnsServer *current_dns_server; - bool read_resolv_conf; + LIST_HEAD(DnsSearchDomain, search_domains); + unsigned n_search_domains; + + bool need_builtin_fallbacks:1; + + bool read_resolv_conf:1; usec_t resolv_conf_mtime; LIST_HEAD(DnsScope, dns_scopes); @@ -112,13 +125,6 @@ int manager_new(Manager **ret); Manager* manager_free(Manager *m); int manager_start(Manager *m); -int manager_read_resolv_conf(Manager *m); -int manager_write_resolv_conf(Manager *m); - -DnsServer *manager_set_dns_server(Manager *m, DnsServer *s); -DnsServer *manager_find_dns_server(Manager *m, int family, const union in_addr_union *in_addr); -DnsServer *manager_get_dns_server(Manager *m); -void manager_next_dns_server(Manager *m); uint32_t manager_find_mtu(Manager *m); @@ -137,13 +143,14 @@ DnsScope* manager_find_scope(Manager *m, DnsPacket *p); void manager_verify_all(Manager *m); -void manager_flush_dns_servers(Manager *m, DnsServerType t); - DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free); #define EXTRA_CMSG_SPACE 1024 int manager_is_own_hostname(Manager *m, const char *name); +int manager_compile_dns_servers(Manager *m, OrderedSet **servers); +int manager_compile_search_domains(Manager *m, OrderedSet **domains); + const char* support_to_string(Support p) _const_; int support_from_string(const char *s) _pure_; diff --git a/src/resolve/resolved-resolv-conf.c b/src/resolve/resolved-resolv-conf.c new file mode 100644 index 0000000000..956f380f3c --- /dev/null +++ b/src/resolve/resolved-resolv-conf.c @@ -0,0 +1,273 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2014 Tom Gundersen <teg@jklm.no> + + 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 <resolv.h> + +#include "alloc-util.h" +#include "dns-domain.h" +#include "fd-util.h" +#include "fileio-label.h" +#include "fileio.h" +#include "ordered-set.h" +#include "resolved-conf.h" +#include "resolved-resolv-conf.h" +#include "string-util.h" +#include "strv.h" + +int manager_read_resolv_conf(Manager *m) { + _cleanup_fclose_ FILE *f = NULL; + struct stat st, own; + char line[LINE_MAX]; + usec_t t; + int r; + + assert(m); + + /* Reads the system /etc/resolv.conf, if it exists and is not + * symlinked to our own resolv.conf instance */ + + if (!m->read_resolv_conf) + return 0; + + r = stat("/etc/resolv.conf", &st); + if (r < 0) { + if (errno == ENOENT) + return 0; + + r = log_warning_errno(errno, "Failed to stat /etc/resolv.conf: %m"); + goto clear; + } + + /* Have we already seen the file? */ + t = timespec_load(&st.st_mtim); + if (t == m->resolv_conf_mtime) + return 0; + + /* Is it symlinked to our own file? */ + if (stat("/run/systemd/resolve/resolv.conf", &own) >= 0 && + st.st_dev == own.st_dev && + st.st_ino == own.st_ino) + return 0; + + f = fopen("/etc/resolv.conf", "re"); + if (!f) { + if (errno == ENOENT) + return 0; + + r = log_warning_errno(errno, "Failed to open /etc/resolv.conf: %m"); + goto clear; + } + + if (fstat(fileno(f), &st) < 0) { + r = log_error_errno(errno, "Failed to stat open file: %m"); + goto clear; + } + + dns_server_mark_all(m->dns_servers); + dns_search_domain_mark_all(m->search_domains); + + FOREACH_LINE(line, f, r = -errno; goto clear) { + const char *a; + char *l; + + l = strstrip(line); + if (*l == '#' || *l == ';') + continue; + + a = first_word(l, "nameserver"); + if (a) { + r = manager_add_dns_server_by_string(m, DNS_SERVER_SYSTEM, a); + if (r < 0) + log_warning_errno(r, "Failed to parse DNS server address '%s', ignoring.", a); + + continue; + } + + a = first_word(l, "domain"); + if (!a) /* We treat "domain" lines, and "search" lines as equivalent, and add both to our list. */ + a = first_word(l, "search"); + if (a) { + r = manager_parse_search_domains_and_warn(m, a); + if (r < 0) + log_warning_errno(r, "Failed to parse search domain string '%s', ignoring.", a); + } + } + + m->resolv_conf_mtime = t; + + /* Flush out all servers and search domains that are still + * marked. Those are then ones that didn't appear in the new + * /etc/resolv.conf */ + dns_server_unlink_marked(m->dns_servers); + dns_search_domain_unlink_marked(m->search_domains); + + /* Whenever /etc/resolv.conf changes, start using the first + * DNS server of it. This is useful to deal with broken + * network managing implementations (like NetworkManager), + * that when connecting to a VPN place both the VPN DNS + * servers and the local ones in /etc/resolv.conf. Without + * resetting the DNS server to use back to the first entry we + * will continue to use the local one thus being unable to + * resolve VPN domains. */ + manager_set_dns_server(m, m->dns_servers); + + /* Unconditionally flush the cache when /etc/resolv.conf is + * modified, even if the data it contained was completely + * identical to the previous version we used. We do this + * because altering /etc/resolv.conf is typically done when + * the network configuration changes, and that should be + * enough to flush the global unicast DNS cache. */ + if (m->unicast_scope) + dns_cache_flush(&m->unicast_scope->cache); + + return 0; + +clear: + dns_server_unlink_all(m->dns_servers); + dns_search_domain_unlink_all(m->search_domains); + return r; +} + +static void write_resolv_conf_server(DnsServer *s, FILE *f, unsigned *count) { + _cleanup_free_ char *t = NULL; + int r; + + assert(s); + assert(f); + assert(count); + + r = in_addr_to_string(s->family, &s->address, &t); + if (r < 0) { + log_warning_errno(r, "Invalid DNS address. Ignoring: %m"); + return; + } + + if (*count == MAXNS) + fputs("# Too many DNS servers configured, the following entries may be ignored.\n", f); + (*count) ++; + + fprintf(f, "nameserver %s\n", t); +} + +static void write_resolv_conf_search( + const char *domain, + FILE *f, + unsigned *count, + unsigned *length) { + + assert(domain); + assert(f); + assert(length); + + if (*count >= MAXDNSRCH || + *length + strlen(domain) > 256) { + if (*count == MAXDNSRCH) + fputs(" # Too many search domains configured, remaining ones ignored.", f); + if (*length <= 256) + fputs(" # Total length of all search domains is too long, remaining ones ignored.", f); + + return; + } + + (*length) += strlen(domain); + (*count) ++; + + fputc(' ', f); + fputs(domain, f); +} + +static int write_resolv_conf_contents(FILE *f, OrderedSet *dns, OrderedSet *domains) { + Iterator i; + + fputs("# This file is managed by systemd-resolved(8). Do not edit.\n#\n" + "# Third party programs must not access this file directly, but\n" + "# only through the symlink at /etc/resolv.conf. To manage\n" + "# resolv.conf(5) in a different way, replace the symlink by a\n" + "# static file or a different symlink.\n\n", f); + + if (ordered_set_isempty(dns)) + fputs("# No DNS servers known.\n", f); + else { + unsigned count = 0; + DnsServer *s; + + ORDERED_SET_FOREACH(s, dns, i) + write_resolv_conf_server(s, f, &count); + } + + if (!ordered_set_isempty(domains)) { + unsigned length = 0, count = 0; + char *domain; + + fputs("search", f); + ORDERED_SET_FOREACH(domain, domains, i) + write_resolv_conf_search(domain, f, &count, &length); + fputs("\n", f); + } + + return fflush_and_check(f); +} + +int manager_write_resolv_conf(Manager *m) { + + #define PRIVATE_RESOLV_CONF "/run/systemd/resolve/resolv.conf" + + _cleanup_ordered_set_free_ OrderedSet *dns = NULL, *domains = NULL; + _cleanup_free_ char *temp_path = NULL; + _cleanup_fclose_ FILE *f = NULL; + int r; + + assert(m); + + /* Read the system /etc/resolv.conf first */ + manager_read_resolv_conf(m); + + /* Add the full list to a set, to filter out duplicates */ + r = manager_compile_dns_servers(m, &dns); + if (r < 0) + return r; + + r = manager_compile_search_domains(m, &domains); + if (r < 0) + return r; + + r = fopen_temporary_label(PRIVATE_RESOLV_CONF, PRIVATE_RESOLV_CONF, &f, &temp_path); + if (r < 0) + return r; + + fchmod(fileno(f), 0644); + + r = write_resolv_conf_contents(f, dns, domains); + if (r < 0) + goto fail; + + if (rename(temp_path, PRIVATE_RESOLV_CONF) < 0) { + r = -errno; + goto fail; + } + + return 0; + +fail: + (void) unlink(PRIVATE_RESOLV_CONF); + (void) unlink(temp_path); + return r; +} diff --git a/src/core/dbus-snapshot.h b/src/resolve/resolved-resolv-conf.h index 9288f44e15..a3355e994b 100644 --- a/src/core/dbus-snapshot.h +++ b/src/resolve/resolved-resolv-conf.h @@ -5,7 +5,7 @@ /*** This file is part of systemd. - Copyright 2010 Lennart Poettering + Copyright 2014 Tom Gundersen <teg@jklm.no> 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 @@ -21,8 +21,7 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include "sd-bus.h" +#include "resolved-manager.h" -extern const sd_bus_vtable bus_snapshot_vtable[]; - -int bus_snapshot_method_remove(sd_bus_message *message, void *userdata, sd_bus_error *error); +int manager_read_resolv_conf(Manager *m); +int manager_write_resolv_conf(Manager *m); diff --git a/src/resolve/resolved.c b/src/resolve/resolved.c index 7ba0546f4a..be406b71fe 100644 --- a/src/resolve/resolved.c +++ b/src/resolve/resolved.c @@ -26,6 +26,7 @@ #include "mkdir.h" #include "resolved-conf.h" #include "resolved-manager.h" +#include "resolved-resolv-conf.h" #include "selinux-util.h" #include "signal-util.h" #include "user-util.h" @@ -81,8 +82,10 @@ int main(int argc, char *argv[]) { } r = manager_parse_config_file(m); - if (r < 0) - log_warning_errno(r, "Failed to parse configuration file: %m"); + if (r < 0) { + log_error_errno(r, "Failed to parse configuration file: %m"); + goto finish; + } r = manager_start(m); if (r < 0) { diff --git a/src/resolve/resolved.conf.in b/src/resolve/resolved.conf.in index 3eb19e42b7..39ecf83217 100644 --- a/src/resolve/resolved.conf.in +++ b/src/resolve/resolved.conf.in @@ -14,4 +14,5 @@ [Resolve] #DNS= #FallbackDNS=@DNS_SERVERS@ +#Domains= #LLMNR=yes diff --git a/src/run/run.c b/src/run/run.c index 38a482bb11..e1accc467b 100644 --- a/src/run/run.c +++ b/src/run/run.c @@ -33,6 +33,7 @@ #include "event-util.h" #include "fd-util.h" #include "formats-util.h" +#include "parse-util.h" #include "path-util.h" #include "ptyfwd.h" #include "signal-util.h" @@ -41,7 +42,6 @@ #include "terminal-util.h" #include "unit-name.h" #include "user-util.h" -#include "parse-util.h" static bool arg_ask_password = true; static bool arg_scope = false; @@ -648,6 +648,11 @@ static int transient_timer_set_properties(sd_bus_message *m) { if (r < 0) return r; + /* Automatically clean up our transient timers */ + r = sd_bus_message_append(m, "(sv)", "RemainAfterElapse", "b", false); + if (r < 0) + return r; + if (arg_on_active) { r = sd_bus_message_append(m, "(sv)", "OnActiveSec", "t", arg_on_active); if (r < 0) @@ -687,6 +692,51 @@ static int transient_timer_set_properties(sd_bus_message *m) { return 0; } +static int make_unit_name(sd_bus *bus, UnitType t, char **ret) { + const char *unique, *id; + char *p; + int r; + + assert(bus); + assert(t >= 0); + assert(t < _UNIT_TYPE_MAX); + + r = sd_bus_get_unique_name(bus, &unique); + if (r < 0) { + sd_id128_t rnd; + + /* We couldn't get the unique name, which is a pretty + * common case if we are connected to systemd + * directly. In that case, just pick a random uuid as + * name */ + + r = sd_id128_randomize(&rnd); + if (r < 0) + return log_error_errno(r, "Failed to generate random run unit name: %m"); + + if (asprintf(ret, "run-r" SD_ID128_FORMAT_STR ".%s", SD_ID128_FORMAT_VAL(rnd), unit_type_to_string(t)) < 0) + return log_oom(); + + return 0; + } + + /* We managed to get the unique name, then let's use that to + * name our transient units. */ + + id = startswith(unique, ":1."); + if (!id) { + log_error("Unique name %s has unexpected format.", unique); + return -EINVAL; + } + + p = strjoin("run-u", id, ".", unit_type_to_string(t), NULL); + if (!p) + return log_oom(); + + *ret = p; + return 0; +} + static int start_transient_service( sd_bus *bus, char **argv) { @@ -763,8 +813,11 @@ static int start_transient_service( r = unit_name_mangle_with_suffix(arg_unit, UNIT_NAME_NOGLOB, ".service", &service); if (r < 0) return log_error_errno(r, "Failed to mangle unit name: %m"); - } else if (asprintf(&service, "run-"PID_FMT".service", getpid()) < 0) - return log_oom(); + } else { + r = make_unit_name(bus, UNIT_SERVICE, &service); + if (r < 0) + return r; + } r = sd_bus_message_new_method_call( bus, @@ -882,8 +935,11 @@ static int start_transient_scope( r = unit_name_mangle_with_suffix(arg_unit, UNIT_NAME_NOGLOB, ".scope", &scope); if (r < 0) return log_error_errno(r, "Failed to mangle scope name: %m"); - } else if (asprintf(&scope, "run-"PID_FMT".scope", getpid()) < 0) - return log_oom(); + } else { + r = make_unit_name(bus, UNIT_SCOPE, &scope); + if (r < 0) + return r; + } r = sd_bus_message_new_method_call( bus, @@ -1052,9 +1108,15 @@ static int start_transient_timer( break; } - } else if ((asprintf(&service, "run-"PID_FMT".service", getpid()) < 0) || - (asprintf(&timer, "run-"PID_FMT".timer", getpid()) < 0)) - return log_oom(); + } else { + r = make_unit_name(bus, UNIT_SERVICE, &service); + if (r < 0) + return r; + + r = unit_name_change_suffix(service, ".timer", &timer); + if (r < 0) + return log_error_errno(r, "Failed to change unit suffix: %m"); + } r = sd_bus_message_new_method_call( bus, diff --git a/src/shared/acl-util.c b/src/shared/acl-util.c index 79f5a60579..35f2e1b67d 100644 --- a/src/shared/acl-util.c +++ b/src/shared/acl-util.c @@ -22,8 +22,8 @@ #include <errno.h> #include <stdbool.h> -#include "alloc-util.h" #include "acl-util.h" +#include "alloc-util.h" #include "string-util.h" #include "strv.h" #include "user-util.h" diff --git a/src/shared/acl-util.h b/src/shared/acl-util.h index cf612e8722..256a6a5900 100644 --- a/src/shared/acl-util.h +++ b/src/shared/acl-util.h @@ -23,9 +23,9 @@ #ifdef HAVE_ACL +#include <acl/libacl.h> #include <stdbool.h> #include <sys/acl.h> -#include <acl/libacl.h> #include "macro.h" diff --git a/src/shared/acpi-fpdt.c b/src/shared/acpi-fpdt.c index 8e36067f74..30e03c0652 100644 --- a/src/shared/acpi-fpdt.c +++ b/src/shared/acpi-fpdt.c @@ -25,8 +25,8 @@ #include <string.h> #include <unistd.h> -#include "alloc-util.h" #include "acpi-fpdt.h" +#include "alloc-util.h" #include "fd-util.h" #include "fileio.h" #include "time-util.h" diff --git a/src/shared/architecture.c b/src/shared/architecture.c index e2efa4272b..73937bd5a7 100644 --- a/src/shared/architecture.c +++ b/src/shared/architecture.c @@ -21,9 +21,9 @@ #include <sys/utsname.h> +#include "architecture.h" #include "string-table.h" #include "string-util.h" -#include "architecture.h" int uname_architecture(void) { diff --git a/src/shared/boot-timestamps.c b/src/shared/boot-timestamps.c index ecbe1aaa0f..879aca9374 100644 --- a/src/shared/boot-timestamps.c +++ b/src/shared/boot-timestamps.c @@ -20,8 +20,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include "boot-timestamps.h" #include "acpi-fpdt.h" +#include "boot-timestamps.h" #include "efivars.h" int boot_timestamps(const dual_timestamp *n, dual_timestamp *firmware, dual_timestamp *loader) { diff --git a/src/shared/bus-util.c b/src/shared/bus-util.c index a13991a960..8775808da4 100644 --- a/src/shared/bus-util.c +++ b/src/shared/bus-util.c @@ -1428,16 +1428,36 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen return bus_log_create_error(r); return 0; + } else if (streq(field, "EnvironmentFile")) { + r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, "EnvironmentFiles"); if (r < 0) - return r; + return bus_log_create_error(r); r = sd_bus_message_append(m, "v", "a(sb)", 1, eq[0] == '-' ? eq + 1 : eq, eq[0] == '-'); if (r < 0) - return r; + return bus_log_create_error(r); + + return 0; + + } else if (streq(field, "RandomizedDelaySec")) { + usec_t t; + + r = parse_sec(eq, &t); + if (r < 0) + return log_error_errno(r, "Failed to parse RandomizedDelaySec= parameter: %s", eq); + + r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, "RandomizedDelayUSec"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append(m, "v", "t", t); + if (r < 0) + return bus_log_create_error(r); + return 0; } @@ -1450,13 +1470,11 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen "SendSIGHUP", "SendSIGKILL", "WakeSystem", "DefaultDependencies", "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "RemainAfterExit", "PrivateTmp", "PrivateDevices", "PrivateNetwork", "NoNewPrivileges", - "SyslogLevelPrefix", "Delegate")) { + "SyslogLevelPrefix", "Delegate", "RemainAfterElapse")) { r = parse_boolean(eq); - if (r < 0) { - log_error("Failed to parse boolean assignment %s.", assignment); - return -EINVAL; - } + if (r < 0) + return log_error_errno(r, "Failed to parse boolean assignment %s.", assignment); r = sd_bus_message_append(m, "v", "b", r); @@ -1654,7 +1672,7 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen r = sd_bus_message_append(m, "v", "i", i); - } else if (streq(field, "Environment")) { + } else if (STR_IN_SET(field, "Environment", "PassEnvironment")) { const char *p; r = sd_bus_message_open_container(m, 'v', "as"); @@ -1678,9 +1696,16 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen if (r == 0) break; - if (!env_assignment_is_valid(word)) { - log_error("Invalid environment assignment: %s", eq); - return -EINVAL; + if (streq(field, "Environment")) { + if (!env_assignment_is_valid(word)) { + log_error("Invalid environment assignment: %s", word); + return -EINVAL; + } + } else { /* PassEnvironment */ + if (!env_name_is_valid(word)) { + log_error("Invalid environment variable name: %s", word); + return -EINVAL; + } } r = sd_bus_message_append_basic(m, 's', word); diff --git a/src/shared/cgroup-show.h b/src/shared/cgroup-show.h index aa832454b5..5842bdd15e 100644 --- a/src/shared/cgroup-show.h +++ b/src/shared/cgroup-show.h @@ -23,6 +23,7 @@ #include <stdbool.h> #include <sys/types.h> + #include "logs-show.h" int show_cgroup_by_path(const char *path, const char *prefix, unsigned columns, bool kernel_threads, OutputFlags flags); diff --git a/src/shared/clean-ipc.c b/src/shared/clean-ipc.c index 835fe52423..71cc613704 100644 --- a/src/shared/clean-ipc.c +++ b/src/shared/clean-ipc.c @@ -29,13 +29,13 @@ #include <sys/stat.h> #include "clean-ipc.h" +#include "dirent-util.h" #include "fd-util.h" #include "fileio.h" #include "formats-util.h" #include "string-util.h" #include "strv.h" #include "util.h" -#include "dirent-util.h" static int clean_sysvipc_shm(uid_t delete_uid) { _cleanup_fclose_ FILE *f = NULL; diff --git a/src/shared/conf-parser.h b/src/shared/conf-parser.h index fb0234baae..2872b22d9d 100644 --- a/src/shared/conf-parser.h +++ b/src/shared/conf-parser.h @@ -21,8 +21,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <stdio.h> #include <stdbool.h> +#include <stdio.h> #include "macro.h" diff --git a/src/shared/dns-domain.c b/src/shared/dns-domain.c index 7af15e0098..4cf6355b71 100644 --- a/src/shared/dns-domain.c +++ b/src/shared/dns-domain.c @@ -29,6 +29,8 @@ #include "hexdecoct.h" #include "parse-util.h" #include "string-util.h" +#include "strv.h" +#include "utf8.h" int dns_label_unescape(const char **name, char *dest, size_t sz) { const char *n; @@ -180,30 +182,31 @@ int dns_label_unescape_suffix(const char *name, const char **label_terminal, cha return r; } -int dns_label_escape(const char *p, size_t l, char **ret) { - _cleanup_free_ char *s = NULL; +int dns_label_escape(const char *p, size_t l, char *dest, size_t sz) { char *q; - int r; - - assert(p); - assert(ret); if (l > DNS_LABEL_MAX) return -EINVAL; + if (sz < 1) + return -ENOSPC; - s = malloc(l * 4 + 1); - if (!s) - return -ENOMEM; + assert(p); + assert(dest); - q = s; + q = dest; while (l > 0) { if (*p == '.' || *p == '\\') { + if (sz < 3) + return -ENOSPC; + /* Dot or backslash */ *(q++) = '\\'; *(q++) = *p; + sz -= 2; + } else if (*p == '_' || *p == '-' || (*p >= '0' && *p <= '9') || @@ -211,15 +214,27 @@ int dns_label_escape(const char *p, size_t l, char **ret) { (*p >= 'A' && *p <= 'Z')) { /* Proper character */ + + if (sz < 2) + return -ENOSPC; + *(q++) = *p; + sz -= 1; + } else if ((uint8_t) *p >= (uint8_t) ' ' && *p != 127) { /* Everything else */ + + if (sz < 5) + return -ENOSPC; + *(q++) = '\\'; *(q++) = '0' + (char) ((uint8_t) *p / 100); *(q++) = '0' + (char) (((uint8_t) *p / 10) % 10); *(q++) = '0' + (char) ((uint8_t) *p % 10); + sz -= 4; + } else return -EINVAL; @@ -228,8 +243,28 @@ int dns_label_escape(const char *p, size_t l, char **ret) { } *q = 0; + return (int) (q - dest); +} + +int dns_label_escape_new(const char *p, size_t l, char **ret) { + _cleanup_free_ char *s = NULL; + int r; + + assert(p); + assert(ret); + + if (l > DNS_LABEL_MAX) + return -EINVAL; + + s = new(char, DNS_LABEL_ESCAPED_MAX); + if (!s) + return -ENOMEM; + + r = dns_label_escape(p, l, s, DNS_LABEL_ESCAPED_MAX); + if (r < 0) + return r; + *ret = s; - r = q - s; s = NULL; return r; @@ -349,28 +384,32 @@ int dns_name_concat(const char *a, const char *b, char **_ret) { if (k > 0) r = k; - r = dns_label_escape(label, r, &t); - if (r < 0) - return r; - if (_ret) { - if (!GREEDY_REALLOC(ret, allocated, n + !first + strlen(t) + 1)) + if (!GREEDY_REALLOC(ret, allocated, n + !first + DNS_LABEL_ESCAPED_MAX)) return -ENOMEM; + r = dns_label_escape(label, r, ret + n + !first, DNS_LABEL_ESCAPED_MAX); + if (r < 0) + return r; + if (!first) - ret[n++] = '.'; - else - first = false; + ret[n] = '.'; + } else { + char escaped[DNS_LABEL_ESCAPED_MAX]; - memcpy(ret + n, t, r); + r = dns_label_escape(label, r, escaped, sizeof(escaped)); + if (r < 0) + return r; } + if (!first) + n++; + else + first = false; + n += r; } - if (n > DNS_NAME_MAX) - return -EINVAL; - if (_ret) { if (!GREEDY_REALLOC(ret, allocated, n + 1)) return -ENOMEM; @@ -546,6 +585,73 @@ int dns_name_endswith(const char *name, const char *suffix) { } } +int dns_name_change_suffix(const char *name, const char *old_suffix, const char *new_suffix, char **ret) { + const char *n, *s, *saved_before = NULL, *saved_after = NULL, *prefix; + int r, q, k, w; + + assert(name); + assert(old_suffix); + assert(new_suffix); + assert(ret); + + n = name; + s = old_suffix; + + for (;;) { + char ln[DNS_LABEL_MAX+1], ls[DNS_LABEL_MAX+1]; + + if (!saved_before) + saved_before = n; + + r = dns_label_unescape(&n, ln, sizeof(ln)); + if (r < 0) + return r; + k = dns_label_undo_idna(ln, r, ln, sizeof(ln)); + if (k < 0) + return k; + if (k > 0) + r = k; + + if (!saved_after) + saved_after = n; + + q = dns_label_unescape(&s, ls, sizeof(ls)); + if (q < 0) + return q; + w = dns_label_undo_idna(ls, q, ls, sizeof(ls)); + if (w < 0) + return w; + if (w > 0) + q = w; + + if (r == 0 && q == 0) + break; + if (r == 0 && saved_after == n) { + *ret = NULL; /* doesn't match */ + return 0; + } + + ln[r] = ls[q] = 0; + + if (r != q || strcasecmp(ln, ls)) { + + /* Not the same, let's jump back, and try with the next label again */ + s = old_suffix; + n = saved_after; + saved_after = saved_before = NULL; + } + } + + /* Found it! Now generate the new name */ + prefix = strndupa(name, saved_before - name); + + r = dns_name_concat(prefix, new_suffix, ret); + if (r < 0) + return r; + + return 1; +} + int dns_name_between(const char *a, const char *b, const char *c) { int n; @@ -684,34 +790,283 @@ int dns_name_address(const char *p, int *family, union in_addr_union *address) { return 0; } -int dns_name_root(const char *name) { - char label[DNS_LABEL_MAX+1]; - int r; +bool dns_name_is_root(const char *name) { assert(name); - r = dns_label_unescape(&name, label, sizeof(label)); - if (r < 0) - return r; + /* There are exactly two ways to encode the root domain name: + * as empty string, or with a single dot. */ - return r == 0 && *name == 0; + return STR_IN_SET(name, "", "."); } -int dns_name_single_label(const char *name) { +bool dns_name_is_single_label(const char *name) { char label[DNS_LABEL_MAX+1]; int r; assert(name); r = dns_label_unescape(&name, label, sizeof(label)); + if (r <= 0) + return false; + + return dns_name_is_root(name); +} + +/* Encode a domain name according to RFC 1035 Section 3.1 */ +int dns_name_to_wire_format(const char *domain, uint8_t *buffer, size_t len) { + uint8_t *label_length; + uint8_t *out; + int r; + + assert_return(buffer, -EINVAL); + assert_return(domain, -EINVAL); + assert_return(domain[0], -EINVAL); + + out = buffer; + + do { + /* reserve a byte for label length */ + if (len == 0) + return -ENOBUFS; + len--; + label_length = out; + out++; + + /* convert and copy a single label */ + r = dns_label_unescape(&domain, (char *) out, len); + if (r < 0) + return r; + + /* fill label length, move forward */ + *label_length = r; + out += r; + len -= r; + } while (r != 0); + + return out - buffer; +} + +static bool srv_type_label_is_valid(const char *label, size_t n) { + size_t k; + + assert(label); + + if (n < 2) /* Label needs to be at least 2 chars long */ + return false; + + if (label[0] != '_') /* First label char needs to be underscore */ + return false; + + /* Second char must be a letter */ + if (!(label[1] >= 'A' && label[1] <= 'Z') && + !(label[1] >= 'a' && label[1] <= 'z')) + return false; + + /* Third and further chars must be alphanumeric or a hyphen */ + for (k = 2; k < n; k++) { + if (!(label[k] >= 'A' && label[k] <= 'Z') && + !(label[k] >= 'a' && label[k] <= 'z') && + !(label[k] >= '0' && label[k] <= '9') && + label[k] != '-') + return false; + } + + return true; +} + +bool dns_srv_type_is_valid(const char *name) { + unsigned c = 0; + int r; + + if (!name) + return false; + + for (;;) { + char label[DNS_LABEL_MAX]; + + /* This more or less implements RFC 6335, Section 5.1 */ + + r = dns_label_unescape(&name, label, sizeof(label)); + if (r < 0) + return false; + if (r == 0) + break; + + if (c >= 2) + return false; + + if (!srv_type_label_is_valid(label, r)) + return false; + + c++; + } + + return c == 2; /* exactly two labels */ +} + +bool dns_service_name_is_valid(const char *name) { + size_t l; + + /* This more or less implements RFC 6763, Section 4.1.1 */ + + if (!name) + return false; + + if (!utf8_is_valid(name)) + return false; + + if (string_has_cc(name, NULL)) + return false; + + l = strlen(name); + if (l <= 0) + return false; + if (l > 63) + return false; + + return true; +} + +int dns_service_join(const char *name, const char *type, const char *domain, char **ret) { + char escaped[DNS_LABEL_ESCAPED_MAX]; + _cleanup_free_ char *n = NULL; + int r; + + assert(type); + assert(domain); + assert(ret); + + if (!dns_srv_type_is_valid(type)) + return -EINVAL; + + if (!name) + return dns_name_concat(type, domain, ret); + + if (!dns_service_name_is_valid(name)) + return -EINVAL; + + r = dns_label_escape(name, strlen(name), escaped, sizeof(escaped)); if (r < 0) return r; - if (r == 0) - return 0; - r = dns_label_unescape(&name, label, sizeof(label)); + r = dns_name_concat(type, domain, &n); if (r < 0) return r; - return r == 0 && *name == 0; + return dns_name_concat(escaped, n, ret); +} + +static bool dns_service_name_label_is_valid(const char *label, size_t n) { + char *s; + + assert(label); + + if (memchr(label, 0, n)) + return false; + + s = strndupa(label, n); + return dns_service_name_is_valid(s); +} + +int dns_service_split(const char *joined, char **_name, char **_type, char **_domain) { + _cleanup_free_ char *name = NULL, *type = NULL, *domain = NULL; + const char *p = joined, *q = NULL, *d = NULL; + char a[DNS_LABEL_MAX], b[DNS_LABEL_MAX], c[DNS_LABEL_MAX]; + int an, bn, cn, r; + unsigned x = 0; + + assert(joined); + + /* Get first label from the full name */ + an = dns_label_unescape(&p, a, sizeof(a)); + if (an < 0) + return an; + + if (an > 0) { + x++; + + /* If there was a first label, try to get the second one */ + bn = dns_label_unescape(&p, b, sizeof(b)); + if (bn < 0) + return bn; + + if (bn > 0) { + x++; + + /* If there was a second label, try to get the third one */ + q = p; + cn = dns_label_unescape(&p, c, sizeof(c)); + if (cn < 0) + return cn; + + if (cn > 0) + x++; + } else + cn = 0; + } else + an = 0; + + if (x >= 2 && srv_type_label_is_valid(b, bn)) { + + if (x >= 3 && srv_type_label_is_valid(c, cn)) { + + if (dns_service_name_label_is_valid(a, an)) { + + /* OK, got <name> . <type> . <type2> . <domain> */ + + name = strndup(a, an); + if (!name) + return -ENOMEM; + + type = new(char, bn+1+cn+1); + if (!type) + return -ENOMEM; + strcpy(stpcpy(stpcpy(type, b), "."), c); + + d = p; + goto finish; + } + + } else if (srv_type_label_is_valid(a, an)) { + + /* OK, got <type> . <type2> . <domain> */ + + name = NULL; + + type = new(char, an+1+bn+1); + if (!type) + return -ENOMEM; + strcpy(stpcpy(stpcpy(type, a), "."), b); + + d = q; + goto finish; + } + } + + name = NULL; + type = NULL; + d = joined; + +finish: + r = dns_name_normalize(d, &domain); + if (r < 0) + return r; + + if (_domain) { + *_domain = domain; + domain = NULL; + } + + if (_type) { + *_type = type; + type = NULL; + } + + if (_name) { + *_name = name; + name = NULL; + } + + return 0; } diff --git a/src/shared/dns-domain.h b/src/shared/dns-domain.h index 1f0d242c18..99c72574db 100644 --- a/src/shared/dns-domain.h +++ b/src/shared/dns-domain.h @@ -26,11 +26,12 @@ #include "in-addr-util.h" #define DNS_LABEL_MAX 63 -#define DNS_NAME_MAX 255 +#define DNS_LABEL_ESCAPED_MAX (DNS_LABEL_MAX*4+1) int dns_label_unescape(const char **name, char *dest, size_t sz); int dns_label_unescape_suffix(const char *name, const char **label_end, char *dest, size_t sz); -int dns_label_escape(const char *p, size_t l, char **ret); +int dns_label_escape(const char *p, size_t l, char *dest, size_t sz); +int dns_label_escape_new(const char *p, size_t l, char **ret); int dns_label_apply_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max); int dns_label_undo_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max); @@ -62,8 +63,18 @@ int dns_name_between(const char *a, const char *b, const char *c); int dns_name_equal(const char *x, const char *y); int dns_name_endswith(const char *name, const char *suffix); +int dns_name_change_suffix(const char *name, const char *old_suffix, const char *new_suffix, char **ret); + int dns_name_reverse(int family, const union in_addr_union *a, char **ret); int dns_name_address(const char *p, int *family, union in_addr_union *a); -int dns_name_root(const char *name); -int dns_name_single_label(const char *name); +bool dns_name_is_root(const char *name); +bool dns_name_is_single_label(const char *name); + +int dns_name_to_wire_format(const char *domain, uint8_t *buffer, size_t len); + +bool dns_srv_type_is_valid(const char *name); +bool dns_service_name_is_valid(const char *name); + +int dns_service_join(const char *name, const char *type, const char *domain, char **ret); +int dns_service_split(const char *joined, char **name, char **type, char **domain); diff --git a/src/shared/efivars.c b/src/shared/efivars.c index 86bb0b57c3..89deeb9b55 100644 --- a/src/shared/efivars.c +++ b/src/shared/efivars.c @@ -19,9 +19,9 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <unistd.h> -#include <string.h> #include <fcntl.h> +#include <string.h> +#include <unistd.h> #include "alloc-util.h" #include "dirent-util.h" diff --git a/src/shared/efivars.h b/src/shared/efivars.h index e953a12737..5cb4c3af4e 100644 --- a/src/shared/efivars.h +++ b/src/shared/efivars.h @@ -24,6 +24,7 @@ #include <stdbool.h> #include "sd-id128.h" + #include "time-util.h" #define EFI_VENDOR_LOADER SD_ID128_MAKE(4a,67,b0,82,0a,4c,41,cf,b6,c7,44,0b,29,bb,8c,4f) diff --git a/src/shared/firewall-util.c b/src/shared/firewall-util.c index e178287872..5acfb0191b 100644 --- a/src/shared/firewall-util.c +++ b/src/shared/firewall-util.c @@ -19,9 +19,9 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <sys/types.h> #include <arpa/inet.h> #include <net/if.h> +#include <sys/types.h> #include <linux/netfilter_ipv4/ip_tables.h> #include <linux/netfilter/nf_nat.h> #include <linux/netfilter/xt_addrtype.h> diff --git a/src/shared/generator.c b/src/shared/generator.c index cb4ebc606e..9998c64416 100644 --- a/src/shared/generator.c +++ b/src/shared/generator.c @@ -64,7 +64,7 @@ static int write_fsck_sysroot_service(const char *dir, const char *what) { "Description=File System Check on %2$s\n" "DefaultDependencies=no\n" "BindsTo=%3$s\n" - "After=%3$s\n" + "After=%3$s local-fs-pre.target\n" "Before=shutdown.target\n" "\n" "[Service]\n" @@ -142,7 +142,7 @@ int generator_write_fsck_deps( } fprintf(f, - "RequiresOverridable=%1$s\n" + "Requires=%1$s\n" "After=%1$s\n", fsck); } diff --git a/src/shared/install-printf.c b/src/shared/install-printf.c index e1cb5d27ff..74b909d34d 100644 --- a/src/shared/install-printf.c +++ b/src/shared/install-printf.c @@ -67,42 +67,28 @@ static int specifier_instance(char specifier, void *data, void *userdata, char * } static int specifier_user_name(char specifier, void *data, void *userdata, char **ret) { - UnitFileInstallInfo *i = userdata; - const char *username; - _cleanup_free_ char *tmp = NULL; - char *printed = NULL; - - assert(i); + char *t; - if (i->user) - username = i->user; - else - /* get USER env from env or our own uid */ - username = tmp = getusername_malloc(); - - switch (specifier) { - case 'u': - printed = strdup(username); - break; - case 'U': { - /* fish username from passwd */ - uid_t uid; - int r; - - r = get_user_creds(&username, &uid, NULL, NULL, NULL); - if (r < 0) - return r; - - if (asprintf(&printed, UID_FMT, uid) < 0) - return -ENOMEM; - break; - }} + /* If we are UID 0 (root), this will not result in NSS, + * otherwise it might. This is good, as we want to be able to + * run this in PID 1, where our user ID is 0, but where NSS + * lookups are not allowed. */ + t = getusername_malloc(); + if (!t) + return -ENOMEM; - *ret = printed; + *ret = t; return 0; } +static int specifier_user_id(char specifier, void *data, void *userdata, char **ret) { + + if (asprintf(ret, UID_FMT, getuid()) < 0) + return -ENOMEM; + + return 0; +} int install_full_printf(UnitFileInstallInfo *i, const char *format, char **ret) { @@ -114,8 +100,8 @@ int install_full_printf(UnitFileInstallInfo *i, const char *format, char **ret) * %p: the prefix (foo) * %i: the instance (bar) - * %U the UID of the configured user or running user - * %u the username of the configured user or running user + * %U the UID of the running user + * %u the username of running user * %m the machine ID of the running system * %H the host name of the running system * %b the boot ID of the running system @@ -128,7 +114,7 @@ int install_full_printf(UnitFileInstallInfo *i, const char *format, char **ret) { 'p', specifier_prefix, NULL }, { 'i', specifier_instance, NULL }, - { 'U', specifier_user_name, NULL }, + { 'U', specifier_user_id, NULL }, { 'u', specifier_user_name, NULL }, { 'm', specifier_machine_id, NULL }, diff --git a/src/shared/install.c b/src/shared/install.c index b7d1d22505..17e03e59cd 100644 --- a/src/shared/install.c +++ b/src/shared/install.c @@ -30,6 +30,7 @@ #include "conf-parser.h" #include "dirent-util.h" #include "fd-util.h" +#include "fileio.h" #include "fs-util.h" #include "hashmap.h" #include "install-printf.h" @@ -46,13 +47,21 @@ #include "unit-name.h" #include "util.h" +#define UNIT_FILE_FOLLOW_SYMLINK_MAX 64 + +typedef enum SearchFlags { + SEARCH_LOAD = 1, + SEARCH_FOLLOW_CONFIG_SYMLINKS = 2, +} SearchFlags; + typedef struct { - OrderedHashmap *will_install; - OrderedHashmap *have_installed; + OrderedHashmap *will_process; + OrderedHashmap *have_processed; } InstallContext; static int in_search_path(const char *path, char **search) { _cleanup_free_ char *parent = NULL; + char **i; assert(path); @@ -60,7 +69,11 @@ static int in_search_path(const char *path, char **search) { if (!parent) return -ENOMEM; - return strv_contains(search, parent); + STRV_FOREACH(i, search) + if (path_equal(parent, *i)) + return true; + + return false; } static int get_config_path(UnitFileScope scope, bool runtime, const char *root_dir, char **ret) { @@ -71,6 +84,9 @@ static int get_config_path(UnitFileScope scope, bool runtime, const char *root_d assert(scope < _UNIT_FILE_SCOPE_MAX); assert(ret); + /* This determines where we shall create or remove our + * installation ("configuration") symlinks */ + switch (scope) { case UNIT_FILE_SYSTEM: @@ -101,9 +117,10 @@ static int get_config_path(UnitFileScope scope, bool runtime, const char *root_d r = user_runtime_dir(&p); else r = user_config_home(&p); - - if (r <= 0) - return r < 0 ? r : -ENOENT; + if (r < 0) + return r; + if (r == 0) + return -ENOENT; break; @@ -118,6 +135,185 @@ static int get_config_path(UnitFileScope scope, bool runtime, const char *root_d return 0; } +static bool is_config_path(UnitFileScope scope, const char *path) { + int r; + + assert(scope >= 0); + assert(scope < _UNIT_FILE_SCOPE_MAX); + assert(path); + + /* Checks whether the specified path is intended for + * configuration or is outside of it */ + + switch (scope) { + + case UNIT_FILE_SYSTEM: + case UNIT_FILE_GLOBAL: + return path_startswith(path, "/etc") || + path_startswith(path, SYSTEM_CONFIG_UNIT_PATH) || + path_startswith(path, "/run"); + + + case UNIT_FILE_USER: { + _cleanup_free_ char *p = NULL; + + r = user_config_home(&p); + if (r < 0) + return r; + if (r > 0 && path_startswith(path, p)) + return true; + + p = mfree(p); + + r = user_runtime_dir(&p); + if (r < 0) + return r; + if (r > 0 && path_startswith(path, p)) + return true; + + return false; + } + + default: + assert_not_reached("Bad scope"); + } +} + + +static int verify_root_dir(UnitFileScope scope, const char **root_dir) { + int r; + + assert(root_dir); + + /* 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 */ + + if (isempty(*root_dir) || path_equal(*root_dir, "/")) { + *root_dir = NULL; + return 0; + } + + if (scope != UNIT_FILE_SYSTEM) + return -EINVAL; + + r = is_dir(*root_dir, true); + if (r < 0) + return r; + if (r == 0) + return -ENOTDIR; + + return 0; +} + +int unit_file_changes_add( + UnitFileChange **changes, + unsigned *n_changes, + UnitFileChangeType type, + const char *path, + const char *source) { + + UnitFileChange *c; + unsigned i; + + assert(path); + assert(!changes == !n_changes); + + if (!changes) + return 0; + + c = realloc(*changes, (*n_changes + 1) * sizeof(UnitFileChange)); + if (!c) + return -ENOMEM; + + *changes = c; + i = *n_changes; + + c[i].type = type; + c[i].path = strdup(path); + if (!c[i].path) + return -ENOMEM; + + path_kill_slashes(c[i].path); + + if (source) { + c[i].source = strdup(source); + if (!c[i].source) { + free(c[i].path); + return -ENOMEM; + } + + path_kill_slashes(c[i].path); + } else + c[i].source = NULL; + + *n_changes = i+1; + return 0; +} + +void unit_file_changes_free(UnitFileChange *changes, unsigned n_changes) { + unsigned i; + + assert(changes || n_changes == 0); + + if (!changes) + return; + + for (i = 0; i < n_changes; i++) { + free(changes[i].path); + free(changes[i].source); + } + + free(changes); +} + +static int create_symlink( + const char *old_path, + const char *new_path, + bool force, + UnitFileChange **changes, + unsigned *n_changes) { + + _cleanup_free_ char *dest = NULL; + int r; + + assert(old_path); + assert(new_path); + + /* Actually create a symlink, and remember that we did. Is + * smart enough to check if there's already a valid symlink in + * place. */ + + mkdir_parents_label(new_path, 0755); + + if (symlink(old_path, new_path) >= 0) { + unit_file_changes_add(changes, n_changes, UNIT_FILE_SYMLINK, new_path, old_path); + return 0; + } + + if (errno != EEXIST) + return -errno; + + r = readlink_malloc(new_path, &dest); + if (r < 0) + return r; + + if (path_equal(dest, old_path)) + return 0; + + if (!force) + return -EEXIST; + + r = symlink_atomic(old_path, new_path); + if (r < 0) + return r; + + unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, new_path, NULL); + unit_file_changes_add(changes, n_changes, UNIT_FILE_SYMLINK, new_path, old_path); + + return 0; +} + static int mark_symlink_for_removal( Set **remove_symlinks_to, const char *p) { @@ -138,10 +334,12 @@ static int mark_symlink_for_removal( path_kill_slashes(n); r = set_consume(*remove_symlinks_to, n); + if (r == -EEXIST) + return 0; if (r < 0) - return r == -EEXIST ? 0 : r; + return r; - return 0; + return 1; } static int remove_marked_symlinks_fd( @@ -149,19 +347,19 @@ static int remove_marked_symlinks_fd( int fd, const char *path, const char *config_path, - bool *deleted, + bool *restart, UnitFileChange **changes, - unsigned *n_changes, - char** instance_whitelist) { + unsigned *n_changes) { _cleanup_closedir_ DIR *d = NULL; + struct dirent *de; int r = 0; assert(remove_symlinks_to); assert(fd >= 0); assert(path); assert(config_path); - assert(deleted); + assert(restart); d = fdopendir(fd); if (!d) { @@ -171,27 +369,13 @@ static int remove_marked_symlinks_fd( rewinddir(d); - for (;;) { - struct dirent *de; - - errno = 0; - de = readdir(d); - if (!de && errno != 0) { - r = -errno; - break; - } - - if (!de) - break; - - if (hidden_file(de->d_name)) - continue; + FOREACH_DIRENT(de, d, return -errno) { dirent_ensure_type(d, de); if (de->d_type == DT_DIR) { - int nfd, q; _cleanup_free_ char *p = NULL; + int nfd, q; nfd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW); if (nfd < 0) { @@ -210,42 +394,23 @@ 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, deleted, changes, n_changes, instance_whitelist); + q = remove_marked_symlinks_fd(remove_symlinks_to, nfd, p, config_path, 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; - int q; bool found; + int q; if (!unit_name_is_valid(de->d_name, UNIT_NAME_ANY)) continue; - if (unit_name_is_valid(de->d_name, UNIT_NAME_INSTANCE) && - instance_whitelist && - !strv_contains(instance_whitelist, de->d_name)) { - - _cleanup_free_ char *w = NULL; - - /* OK, the file is not listed directly - * in the whitelist, so let's check if - * the template of it might be - * listed. */ - - r = unit_name_template(de->d_name, &w); - if (r < 0) - return r; - - if (!strv_contains(instance_whitelist, w)) - continue; - } - p = path_make_absolute(de->d_name, path); if (!p) return -ENOMEM; - q = readlink_and_canonicalize(p, &dest); + q = readlink_malloc(p, &dest); if (q < 0) { if (q == -ENOENT) continue; @@ -255,9 +420,15 @@ static int remove_marked_symlinks_fd( 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. */ + found = - set_get(remove_symlinks_to, dest) || - set_get(remove_symlinks_to, basename(dest)); + set_contains(remove_symlinks_to, dest) || + set_contains(remove_symlinks_to, basename(dest)) || + set_contains(remove_symlinks_to, de->d_name); if (!found) continue; @@ -269,18 +440,15 @@ static int remove_marked_symlinks_fd( } path_kill_slashes(p); - rmdir_parents(p, config_path); - unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, p, NULL); + (void) rmdir_parents(p, config_path); - if (!set_get(remove_symlinks_to, p)) { + unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, p, NULL); - q = mark_symlink_for_removal(&remove_symlinks_to, p); - if (q < 0) { - if (r == 0) - r = q; - } else - *deleted = true; - } + q = mark_symlink_for_removal(&remove_symlinks_to, p); + if (q < 0) + return q; + if (q > 0) + *restart = true; } } @@ -291,12 +459,11 @@ static int remove_marked_symlinks( Set *remove_symlinks_to, const char *config_path, UnitFileChange **changes, - unsigned *n_changes, - char** instance_whitelist) { + unsigned *n_changes) { _cleanup_close_ int fd = -1; + bool restart; int r = 0; - bool deleted; assert(config_path); @@ -309,32 +476,32 @@ static int remove_marked_symlinks( do { int q, cfd; - deleted = false; + restart = false; cfd = fcntl(fd, F_DUPFD_CLOEXEC, 3); - if (cfd < 0) { - r = -errno; - break; - } + if (cfd < 0) + return -errno; /* This takes possession of cfd and closes it */ - q = remove_marked_symlinks_fd(remove_symlinks_to, cfd, config_path, config_path, &deleted, changes, n_changes, instance_whitelist); + q = remove_marked_symlinks_fd(remove_symlinks_to, cfd, config_path, config_path, &restart, changes, n_changes); if (r == 0) r = q; - } while (deleted); + } while (restart); return r; } static int find_symlinks_fd( + const char *root_dir, const char *name, int fd, const char *path, const char *config_path, bool *same_name_link) { - int r = 0; _cleanup_closedir_ DIR *d = NULL; + struct dirent *de; + int r = 0; assert(name); assert(fd >= 0); @@ -348,25 +515,13 @@ static int find_symlinks_fd( return -errno; } - for (;;) { - struct dirent *de; - - errno = 0; - de = readdir(d); - if (!de && errno != 0) - return -errno; - - if (!de) - return r; - - if (hidden_file(de->d_name)) - continue; + FOREACH_DIRENT(de, d, return -errno) { dirent_ensure_type(d, de); if (de->d_type == DT_DIR) { - int nfd, q; _cleanup_free_ char *p = NULL; + int nfd, q; nfd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW); if (nfd < 0) { @@ -385,7 +540,7 @@ static int find_symlinks_fd( } /* This will close nfd, regardless whether it succeeds or not */ - q = find_symlinks_fd(name, nfd, p, config_path, same_name_link); + q = find_symlinks_fd(root_dir, name, nfd, p, config_path, same_name_link); if (q > 0) return 1; if (r == 0) @@ -402,16 +557,27 @@ static int find_symlinks_fd( return -ENOMEM; /* Acquire symlink destination */ - q = readlink_and_canonicalize(p, &dest); + q = readlink_malloc(p, &dest); + if (q == -ENOENT) + continue; if (q < 0) { - if (q == -ENOENT) - continue; - if (r == 0) r = q; continue; } + /* Make absolute */ + if (!path_is_absolute(dest)) { + char *x; + + x = prefix_root(root_dir, dest); + if (!x) + return -ENOMEM; + + free(dest); + dest = x; + } + /* Check if the symlink itself matches what we * are looking for */ if (path_is_absolute(name)) @@ -444,9 +610,12 @@ static int find_symlinks_fd( return 1; } } + + return r; } static int find_symlinks( + const char *root_dir, const char *name, const char *config_path, bool *same_name_link) { @@ -465,7 +634,7 @@ static int find_symlinks( } /* This takes possession of fd and closes it */ - return find_symlinks_fd(name, fd, config_path, config_path, same_name_link); + return find_symlinks_fd(root_dir, name, fd, config_path, config_path, same_name_link); } static int find_symlinks_in_scope( @@ -474,350 +643,59 @@ static int find_symlinks_in_scope( const char *name, UnitFileState *state) { - int r; _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(name); - /* First look in runtime config path */ - r = get_config_path(scope, true, root_dir, &normal_path); + /* 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(name, normal_path, &same_name_link_runtime); + r = find_symlinks(root_dir, name, normal_path, &same_name_link); if (r < 0) return r; - else if (r > 0) { - *state = UNIT_FILE_ENABLED_RUNTIME; + if (r > 0) { + *state = UNIT_FILE_ENABLED; return r; } - /* Then look in the normal config path */ - r = get_config_path(scope, false, root_dir, &runtime_path); + /* Then look in runtime config path */ + r = get_config_path(scope, true, root_dir, &runtime_path); if (r < 0) return r; - r = find_symlinks(name, runtime_path, &same_name_link); + r = find_symlinks(root_dir, name, runtime_path, &same_name_link_runtime); if (r < 0) return r; - else if (r > 0) { - *state = UNIT_FILE_ENABLED; + if (r > 0) { + *state = UNIT_FILE_ENABLED_RUNTIME; return r; } /* Hmm, we didn't find it, but maybe we found the same name * link? */ - if (same_name_link_runtime) { - *state = UNIT_FILE_LINKED_RUNTIME; - return 1; - } else if (same_name_link) { + if (same_name_link) { *state = UNIT_FILE_LINKED; return 1; } - - return 0; -} - -int unit_file_mask( - UnitFileScope scope, - bool runtime, - const char *root_dir, - char **files, - bool force, - UnitFileChange **changes, - unsigned *n_changes) { - - char **i; - _cleanup_free_ char *prefix = NULL; - int r; - - assert(scope >= 0); - assert(scope < _UNIT_FILE_SCOPE_MAX); - - r = get_config_path(scope, runtime, root_dir, &prefix); - if (r < 0) - return r; - - STRV_FOREACH(i, files) { - _cleanup_free_ char *path = NULL; - - if (!unit_name_is_valid(*i, UNIT_NAME_ANY)) { - if (r == 0) - r = -EINVAL; - continue; - } - - path = path_make_absolute(*i, prefix); - if (!path) { - r = -ENOMEM; - break; - } - - if (symlink("/dev/null", path) >= 0) { - unit_file_changes_add(changes, n_changes, UNIT_FILE_SYMLINK, path, "/dev/null"); - continue; - } - - if (errno == EEXIST) { - - if (null_or_empty_path(path) > 0) - continue; - - if (force) { - if (symlink_atomic("/dev/null", path) >= 0) { - unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, path, NULL); - unit_file_changes_add(changes, n_changes, UNIT_FILE_SYMLINK, path, "/dev/null"); - continue; - } - } - - if (r == 0) - r = -EEXIST; - } else { - if (r == 0) - r = -errno; - } - } - - return r; -} - -int unit_file_unmask( - UnitFileScope scope, - bool runtime, - const char *root_dir, - char **files, - UnitFileChange **changes, - unsigned *n_changes) { - - char **i, *config_path = NULL; - int r, q; - Set *remove_symlinks_to = NULL; - - assert(scope >= 0); - assert(scope < _UNIT_FILE_SCOPE_MAX); - - r = get_config_path(scope, runtime, root_dir, &config_path); - if (r < 0) - goto finish; - - STRV_FOREACH(i, files) { - _cleanup_free_ char *path = NULL; - - if (!unit_name_is_valid(*i, UNIT_NAME_ANY)) { - if (r == 0) - r = -EINVAL; - continue; - } - - path = path_make_absolute(*i, config_path); - if (!path) { - r = -ENOMEM; - break; - } - - q = null_or_empty_path(path); - if (q > 0) { - if (unlink(path) < 0) - q = -errno; - else { - q = mark_symlink_for_removal(&remove_symlinks_to, path); - unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, path, NULL); - } - } - - if (q != -ENOENT && r == 0) - r = q; - } - - -finish: - q = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes, files); - if (r == 0) - r = q; - - set_free_free(remove_symlinks_to); - free(config_path); - - return r; -} - -int unit_file_link( - UnitFileScope scope, - bool runtime, - const char *root_dir, - char **files, - bool force, - UnitFileChange **changes, - unsigned *n_changes) { - - _cleanup_lookup_paths_free_ LookupPaths paths = {}; - char **i; - _cleanup_free_ char *config_path = NULL; - int r, q; - - assert(scope >= 0); - assert(scope < _UNIT_FILE_SCOPE_MAX); - - 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; - - STRV_FOREACH(i, files) { - _cleanup_free_ char *path = NULL; - char *fn; - struct stat st; - - fn = basename(*i); - - if (!path_is_absolute(*i) || - !unit_name_is_valid(fn, UNIT_NAME_ANY)) { - if (r == 0) - r = -EINVAL; - continue; - } - - if (lstat(*i, &st) < 0) { - if (r == 0) - r = -errno; - continue; - } - - if (!S_ISREG(st.st_mode)) { - r = -ENOENT; - continue; - } - - q = in_search_path(*i, paths.unit_path); - if (q < 0) - return q; - - if (q > 0) - continue; - - path = path_make_absolute(fn, config_path); - if (!path) - return -ENOMEM; - - if (symlink(*i, path) >= 0) { - unit_file_changes_add(changes, n_changes, UNIT_FILE_SYMLINK, path, *i); - continue; - } - - if (errno == EEXIST) { - _cleanup_free_ char *dest = NULL; - - q = readlink_and_make_absolute(path, &dest); - if (q < 0 && errno != ENOENT) { - if (r == 0) - r = q; - continue; - } - - if (q >= 0 && path_equal(dest, *i)) - continue; - - if (force) { - if (symlink_atomic(*i, path) >= 0) { - unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, path, NULL); - unit_file_changes_add(changes, n_changes, UNIT_FILE_SYMLINK, path, *i); - continue; - } - } - - if (r == 0) - r = -EEXIST; - } else { - if (r == 0) - r = -errno; - } - } - - return r; -} - -void unit_file_list_free(Hashmap *h) { - UnitFileList *i; - - while ((i = hashmap_steal_first(h))) { - free(i->path); - free(i); + if (same_name_link_runtime) { + *state = UNIT_FILE_LINKED_RUNTIME; + return 1; } - hashmap_free(h); -} - -int unit_file_changes_add( - UnitFileChange **changes, - unsigned *n_changes, - UnitFileChangeType type, - const char *path, - const char *source) { - - UnitFileChange *c; - unsigned i; - - assert(path); - assert(!changes == !n_changes); - - if (!changes) - return 0; - - c = realloc(*changes, (*n_changes + 1) * sizeof(UnitFileChange)); - if (!c) - return -ENOMEM; - - *changes = c; - i = *n_changes; - - c[i].type = type; - c[i].path = strdup(path); - if (!c[i].path) - return -ENOMEM; - - path_kill_slashes(c[i].path); - - if (source) { - c[i].source = strdup(source); - if (!c[i].source) { - free(c[i].path); - return -ENOMEM; - } - - path_kill_slashes(c[i].path); - } else - c[i].source = NULL; - - *n_changes = i+1; return 0; } -void unit_file_changes_free(UnitFileChange *changes, unsigned n_changes) { - unsigned i; - - assert(changes || n_changes == 0); +static void install_info_free(UnitFileInstallInfo *i) { - if (!changes) + if (!i) return; - for (i = 0; i < n_changes; i++) { - free(changes[i].path); - free(changes[i].source); - } - - free(changes); -} - -static void install_info_free(UnitFileInstallInfo *i) { - assert(i); - free(i->name); free(i->path); strv_free(i->aliases); @@ -825,34 +703,45 @@ static void install_info_free(UnitFileInstallInfo *i) { strv_free(i->required_by); strv_free(i->also); free(i->default_instance); + free(i->symlink_target); free(i); } -static void install_info_hashmap_free(OrderedHashmap *m) { +static OrderedHashmap* install_info_hashmap_free(OrderedHashmap *m) { UnitFileInstallInfo *i; if (!m) - return; + return NULL; while ((i = ordered_hashmap_steal_first(m))) install_info_free(i); - ordered_hashmap_free(m); + return ordered_hashmap_free(m); } static void install_context_done(InstallContext *c) { assert(c); - install_info_hashmap_free(c->will_install); - install_info_hashmap_free(c->have_installed); + c->will_process = install_info_hashmap_free(c->will_process); + c->have_processed = install_info_hashmap_free(c->have_processed); +} + +static UnitFileInstallInfo *install_info_find(InstallContext *c, const char *name) { + UnitFileInstallInfo *i; + + i = ordered_hashmap_get(c->have_processed, name); + if (i) + return i; - c->will_install = c->have_installed = NULL; + return ordered_hashmap_get(c->will_process, name); } static int install_info_add( InstallContext *c, const char *name, - const char *path) { + const char *path, + UnitFileInstallInfo **ret) { + UnitFileInstallInfo *i = NULL; int r; @@ -865,17 +754,21 @@ static int install_info_add( if (!unit_name_is_valid(name, UNIT_NAME_ANY)) return -EINVAL; - if (ordered_hashmap_get(c->have_installed, name) || - ordered_hashmap_get(c->will_install, name)) + i = install_info_find(c, name); + if (i) { + if (ret) + *ret = i; return 0; + } - r = ordered_hashmap_ensure_allocated(&c->will_install, &string_hash_ops); + r = ordered_hashmap_ensure_allocated(&c->will_process, &string_hash_ops); if (r < 0) return r; i = new0(UnitFileInstallInfo, 1); if (!i) return -ENOMEM; + i->type = _UNIT_FILE_TYPE_INVALID; i->name = strdup(name); if (!i->name) { @@ -891,30 +784,32 @@ static int install_info_add( } } - r = ordered_hashmap_put(c->will_install, i->name, i); + r = ordered_hashmap_put(c->will_process, i->name, i); if (r < 0) goto fail; + if (ret) + *ret = i; + return 0; fail: - if (i) - install_info_free(i); - + install_info_free(i); return r; } static int install_info_add_auto( InstallContext *c, - const char *name_or_path) { + 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); + return install_info_add(c, NULL, name_or_path, ret); else - return install_info_add(c, name_or_path, NULL); + return install_info_add(c, name_or_path, NULL, ret); } static int config_parse_also( @@ -929,64 +824,33 @@ static int config_parse_also( void *data, void *userdata) { - InstallContext *c = data; UnitFileInstallInfo *i = userdata; + InstallContext *c = data; + int r; assert(filename); assert(lvalue); assert(rvalue); - for(;;) { - _cleanup_free_ char *n = NULL; - int r; - - r = extract_first_word(&rvalue, &n, NULL, 0); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse config %s, ignoring.", rvalue); - return 0; - } + for (;;) { + _cleanup_free_ char *word = NULL; + r = extract_first_word(&rvalue, &word, NULL, 0); + if (r < 0) + return r; if (r == 0) break; - r = install_info_add(c, n, NULL); + r = install_info_add(c, word, NULL, NULL); if (r < 0) return r; - r = strv_extend(&i->also, n); + r = strv_push(&i->also, word); if (r < 0) return r; - } - - return 0; -} -static int config_parse_user( - const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - UnitFileInstallInfo *i = data; - char *printed; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - - r = install_full_printf(i, rvalue, &printed); - if (r < 0) - return r; - - free(i->user); - i->user = printed; + word = NULL; + } return 0; } @@ -1031,9 +895,7 @@ static int unit_file_load( UnitFileInstallInfo *info, const char *path, const char *root_dir, - bool allow_symlink, - bool load, - bool *also) { + SearchFlags flags) { const ConfigTableItem items[] = { { "Install", "Alias", config_parse_strv, 0, &info->aliases }, @@ -1041,34 +903,57 @@ static int unit_file_load( { "Install", "RequiredBy", config_parse_strv, 0, &info->required_by }, { "Install", "DefaultInstance", config_parse_default_instance, 0, info }, { "Install", "Also", config_parse_also, 0, c }, - { "Exec", "User", config_parse_user, 0, info }, {} }; _cleanup_fclose_ FILE *f = NULL; - int fd, r; + _cleanup_close_ int fd = -1; + struct stat st; + int r; assert(c); assert(info); assert(path); - if (!isempty(root_dir)) - path = strjoina(root_dir, "/", path); + path = prefix_roota(root_dir, path); - if (!load) { - r = access(path, F_OK) ? -errno : 0; - return r; + if (!(flags & SEARCH_LOAD)) { + r = lstat(path, &st); + if (r < 0) + return -errno; + + if (null_or_empty(&st)) + info->type = UNIT_FILE_TYPE_MASKED; + else if (S_ISREG(st.st_mode)) + info->type = UNIT_FILE_TYPE_REGULAR; + else if (S_ISLNK(st.st_mode)) + return -ELOOP; + else if (S_ISDIR(st.st_mode)) + return -EISDIR; + else + return -ENOTTY; + + return 0; } - fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|(allow_symlink ? 0 : O_NOFOLLOW)); + fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW); if (fd < 0) return -errno; + if (fstat(fd, &st) < 0) + return -errno; + if (null_or_empty(&st)) { + info->type = UNIT_FILE_TYPE_MASKED; + return 0; + } + if (S_ISDIR(st.st_mode)) + return -EISDIR; + if (!S_ISREG(st.st_mode)) + return -ENOTTY; f = fdopen(fd, "re"); - if (!f) { - safe_close(fd); - return -ENOMEM; - } + if (!f) + return -errno; + fd = -1; r = config_parse(NULL, path, f, NULL, @@ -1077,8 +962,7 @@ static int unit_file_load( if (r < 0) return r; - if (also) - *also = !strv_isempty(info->also); + info->type = UNIT_FILE_TYPE_REGULAR; return (int) strv_length(info->aliases) + @@ -1086,14 +970,73 @@ static int unit_file_load( (int) strv_length(info->required_by); } +static int unit_file_load_or_readlink( + InstallContext *c, + UnitFileInstallInfo *info, + const char *path, + const char *root_dir, + SearchFlags flags) { + + _cleanup_free_ char *np = NULL; + int r; + + r = unit_file_load(c, info, path, root_dir, flags); + if (r != -ELOOP) + return r; + + /* This is a symlink, let's read it. */ + + r = readlink_and_make_absolute_root(root_dir, path, &np); + if (r < 0) + return r; + + if (path_equal(np, "/dev/null")) + info->type = UNIT_FILE_TYPE_MASKED; + else { + const char *bn; + UnitType a, b; + + bn = basename(np); + + if (unit_name_is_valid(info->name, UNIT_NAME_PLAIN)) { + + if (!unit_name_is_valid(bn, UNIT_NAME_PLAIN)) + return -EINVAL; + + } else if (unit_name_is_valid(info->name, UNIT_NAME_INSTANCE)) { + + if (!unit_name_is_valid(bn, UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE)) + return -EINVAL; + + } else if (unit_name_is_valid(info->name, UNIT_NAME_TEMPLATE)) { + + if (!unit_name_is_valid(bn, UNIT_NAME_TEMPLATE)) + return -EINVAL; + } else + return -EINVAL; + + /* Enforce that the symlink destination does not + * change the unit file type. */ + + a = unit_name_to_type(info->name); + b = unit_name_to_type(bn); + if (a < 0 || b < 0 || a != b) + return -EINVAL; + + info->type = UNIT_FILE_TYPE_SYMLINK; + info->symlink_target = np; + np = NULL; + } + + return 0; +} + static int unit_file_search( InstallContext *c, UnitFileInstallInfo *info, const LookupPaths *paths, const char *root_dir, - bool allow_symlink, - bool load, - bool *also) { + SearchFlags flags) { char **p; int r; @@ -1102,8 +1045,12 @@ static int unit_file_search( assert(info); assert(paths); + /* Was this unit already loaded? */ + if (info->type != _UNIT_FILE_TYPE_INVALID) + return 0; + if (info->path) - return unit_file_load(c, info, info->path, root_dir, allow_symlink, load, also); + return unit_file_load_or_readlink(c, info, info->path, root_dir, flags); assert(info->name); @@ -1114,14 +1061,15 @@ static int unit_file_search( if (!path) return -ENOMEM; - r = unit_file_load(c, info, path, root_dir, allow_symlink, load, also); - if (r >= 0) { + r = unit_file_load_or_readlink(c, info, path, root_dir, flags); + if (r < 0) { + if (r != -ENOENT) + return r; + } else { info->path = path; path = NULL; return r; } - if (r != -ENOENT && r != -ELOOP) - return r; } if (unit_name_is_valid(info->name, UNIT_NAME_INSTANCE)) { @@ -1143,92 +1091,149 @@ static int unit_file_search( if (!path) return -ENOMEM; - r = unit_file_load(c, info, path, root_dir, allow_symlink, load, also); - if (r >= 0) { + r = unit_file_load_or_readlink(c, info, path, root_dir, flags); + if (r < 0) { + if (r != -ENOENT) + return r; + } else { info->path = path; path = NULL; return r; } - if (r != -ENOENT && r != -ELOOP) - return r; } } return -ENOENT; } -static int unit_file_can_install( - const LookupPaths *paths, +static int install_info_follow( + InstallContext *c, + UnitFileInstallInfo *i, const char *root_dir, - const char *name, - bool allow_symlink, - bool *also) { + SearchFlags flags) { + + assert(c); + assert(i); + + if (i->type != UNIT_FILE_TYPE_SYMLINK) + return -EINVAL; + if (!i->symlink_target) + return -EINVAL; + + /* If the basename doesn't match, the caller should add a + * complete new entry for this. */ + + if (!streq(basename(i->symlink_target), i->name)) + return -EXDEV; + + free(i->path); + i->path = i->symlink_target; + i->symlink_target = NULL; + i->type = _UNIT_FILE_TYPE_INVALID; + + return unit_file_load_or_readlink(c, i, i->path, root_dir, flags); +} + +static int install_info_traverse( + UnitFileScope scope, + InstallContext *c, + const char *root_dir, + const LookupPaths *paths, + UnitFileInstallInfo *start, + SearchFlags flags, + UnitFileInstallInfo **ret) { - _cleanup_(install_context_done) InstallContext c = {}; UnitFileInstallInfo *i; + unsigned k = 0; int r; assert(paths); - assert(name); + assert(start); + assert(c); - r = install_info_add_auto(&c, name); + r = unit_file_search(c, start, paths, root_dir, flags); if (r < 0) return r; - assert_se(i = ordered_hashmap_first(c.will_install)); + i = start; + while (i->type == UNIT_FILE_TYPE_SYMLINK) { + /* Follow the symlink */ - r = unit_file_search(&c, i, paths, root_dir, allow_symlink, true, also); + if (++k > UNIT_FILE_FOLLOW_SYMLINK_MAX) + return -ELOOP; - if (r >= 0) - r = - (int) strv_length(i->aliases) + - (int) strv_length(i->wanted_by) + - (int) strv_length(i->required_by); + if (!(flags & SEARCH_FOLLOW_CONFIG_SYMLINKS) && is_config_path(scope, i->path)) + return -ELOOP; - return r; -} + r = install_info_follow(c, i, root_dir, flags); + if (r < 0) { + _cleanup_free_ char *buffer = NULL; + const char *bn; -static int create_symlink( - const char *old_path, - const char *new_path, - bool force, - UnitFileChange **changes, - unsigned *n_changes) { + if (r != -EXDEV) + return r; - _cleanup_free_ char *dest = NULL; - int r; + /* Target has a different name, create a new + * install info object for that, and continue + * with that. */ - assert(old_path); - assert(new_path); + bn = basename(i->symlink_target); - mkdir_parents_label(new_path, 0755); + if (unit_name_is_valid(i->name, UNIT_NAME_INSTANCE) && + unit_name_is_valid(bn, UNIT_NAME_TEMPLATE)) { - if (symlink(old_path, new_path) >= 0) { - unit_file_changes_add(changes, n_changes, UNIT_FILE_SYMLINK, new_path, old_path); - return 0; + _cleanup_free_ char *instance = NULL; + + r = unit_name_to_instance(i->name, &instance); + if (r < 0) + return r; + + r = unit_name_replace_instance(bn, instance, &buffer); + if (r < 0) + return r; + + bn = buffer; + } + + r = install_info_add(c, bn, NULL, &i); + if (r < 0) + return r; + + r = unit_file_search(c, i, paths, root_dir, flags); + if (r < 0) + return r; + } + + /* Try again, with the new target we found. */ } - if (errno != EEXIST) - return -errno; + if (ret) + *ret = i; - r = readlink_and_make_absolute(new_path, &dest); - if (r < 0) - return r; + return 0; +} - if (path_equal(dest, old_path)) - return 0; +static int install_info_discover( + UnitFileScope scope, + InstallContext *c, + const char *root_dir, + const LookupPaths *paths, + const char *name, + SearchFlags flags, + UnitFileInstallInfo **ret) { - if (!force) - return -EEXIST; + UnitFileInstallInfo *i; + int r; - r = symlink_atomic(old_path, new_path); + assert(c); + assert(paths); + assert(name); + + r = install_info_add_auto(c, name, &i); if (r < 0) return r; - unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, new_path, NULL); - unit_file_changes_add(changes, n_changes, UNIT_FILE_SYMLINK, new_path, old_path); - - return 0; + return install_info_traverse(scope, c, root_dir, paths, i, flags, ret); } static int install_info_symlink_alias( @@ -1363,6 +1368,9 @@ static int install_info_apply( assert(paths); assert(config_path); + if (i->type != UNIT_FILE_TYPE_REGULAR) + return 0; + r = install_info_symlink_alias(i, config_path, force, changes, n_changes); q = install_info_symlink_wants(i, config_path, i->wanted_by, ".wants/", force, changes, n_changes); @@ -1381,53 +1389,59 @@ static int install_info_apply( } static int install_context_apply( + UnitFileScope scope, InstallContext *c, const LookupPaths *paths, const char *config_path, const char *root_dir, bool force, + SearchFlags flags, UnitFileChange **changes, unsigned *n_changes) { UnitFileInstallInfo *i; - int r, q; + int r; assert(c); assert(paths); assert(config_path); - if (!ordered_hashmap_isempty(c->will_install)) { - r = ordered_hashmap_ensure_allocated(&c->have_installed, &string_hash_ops); - if (r < 0) - return r; + if (ordered_hashmap_isempty(c->will_process)) + return 0; - r = ordered_hashmap_reserve(c->have_installed, ordered_hashmap_size(c->will_install)); - if (r < 0) - return r; - } + r = ordered_hashmap_ensure_allocated(&c->have_processed, &string_hash_ops); + if (r < 0) + return r; r = 0; - while ((i = ordered_hashmap_first(c->will_install))) { - assert_se(ordered_hashmap_move_one(c->have_installed, c->will_install, i->name) == 0); + while ((i = ordered_hashmap_first(c->will_process))) { + int q; - q = unit_file_search(c, i, paths, root_dir, false, true, NULL); - if (q < 0) { - if (r >= 0) - r = q; + q = ordered_hashmap_move_one(c->have_processed, c->will_process, i->name); + if (q < 0) + return q; + r = install_info_traverse(scope, c, root_dir, paths, i, flags, NULL); + if (r < 0) return r; - } else if (r >= 0) - r += q; + + if (i->type != UNIT_FILE_TYPE_REGULAR) + continue; q = install_info_apply(i, paths, config_path, root_dir, force, changes, n_changes); - if (r >= 0 && q < 0) - r = q; + if (r >= 0) { + if (q < 0) + r = q; + else + r+= q; + } } return r; } static int install_context_mark_for_removal( + UnitFileScope scope, InstallContext *c, const LookupPaths *paths, Set **remove_symlinks_to, @@ -1435,7 +1449,7 @@ static int install_context_mark_for_removal( const char *root_dir) { UnitFileInstallInfo *i; - int r, q; + int r; assert(c); assert(paths); @@ -1443,87 +1457,182 @@ static int install_context_mark_for_removal( /* Marks all items for removal */ - if (!ordered_hashmap_isempty(c->will_install)) { - r = ordered_hashmap_ensure_allocated(&c->have_installed, &string_hash_ops); + if (ordered_hashmap_isempty(c->will_process)) + return 0; + + r = ordered_hashmap_ensure_allocated(&c->have_processed, &string_hash_ops); + if (r < 0) + return r; + + while ((i = ordered_hashmap_first(c->will_process))) { + + r = ordered_hashmap_move_one(c->have_processed, c->will_process, i->name); if (r < 0) return r; - r = ordered_hashmap_reserve(c->have_installed, ordered_hashmap_size(c->will_install)); + r = install_info_traverse(scope, c, root_dir, paths, i, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, NULL); if (r < 0) return r; - } - r = 0; - while ((i = ordered_hashmap_first(c->will_install))) { - assert_se(ordered_hashmap_move_one(c->have_installed, c->will_install, i->name) == 0); - - q = unit_file_search(c, i, paths, root_dir, false, true, NULL); - if (q == -ENOENT) { - /* do nothing */ - } else if (q < 0) { - if (r >= 0) - r = q; + if (i->type != UNIT_FILE_TYPE_REGULAR) + continue; + r = mark_symlink_for_removal(remove_symlinks_to, i->name); + if (r < 0) return r; - } else if (r >= 0) - r += q; - - if (unit_name_is_valid(i->name, UNIT_NAME_INSTANCE)) { - char *unit_file; - - if (i->path) { - unit_file = basename(i->path); - - if (unit_name_is_valid(unit_file, UNIT_NAME_INSTANCE)) - /* unit file named as instance exists, thus all symlinks - * pointing to it will be removed */ - q = mark_symlink_for_removal(remove_symlinks_to, i->name); - else - /* does not exist, thus we will mark for removal symlinks - * to template unit file */ - q = mark_symlink_for_removal(remove_symlinks_to, unit_file); - } else { - /* If i->path is not set, it means that we didn't actually find - * the unit file. But we can still remove symlinks to the - * nonexistent template. */ - r = unit_name_template(i->name, &unit_file); - if (r < 0) - return r; + } - q = mark_symlink_for_removal(remove_symlinks_to, unit_file); - free(unit_file); - } - } else - q = mark_symlink_for_removal(remove_symlinks_to, i->name); + return 0; +} + +int unit_file_mask( + UnitFileScope scope, + bool runtime, + const char *root_dir, + char **files, + bool force, + UnitFileChange **changes, + unsigned *n_changes) { + + _cleanup_free_ char *prefix = NULL; + char **i; + int r; + + assert(scope >= 0); + assert(scope < _UNIT_FILE_SCOPE_MAX); - if (r >= 0 && q < 0) + r = verify_root_dir(scope, &root_dir); + if (r < 0) + return r; + + r = get_config_path(scope, runtime, root_dir, &prefix); + if (r < 0) + return r; + + STRV_FOREACH(i, files) { + _cleanup_free_ char *path = NULL; + int q; + + if (!unit_name_is_valid(*i, UNIT_NAME_ANY)) { + if (r == 0) + r = -EINVAL; + continue; + } + + path = path_make_absolute(*i, prefix); + if (!path) + return -ENOMEM; + + q = create_symlink("/dev/null", path, force, changes, n_changes); + if (q < 0 && r >= 0) r = q; } return r; } -int unit_file_add_dependency( +int unit_file_unmask( + UnitFileScope scope, + bool runtime, + const char *root_dir, + char **files, + UnitFileChange **changes, + unsigned *n_changes) { + + _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; + char **i; + int r, q; + + assert(scope >= 0); + assert(scope < _UNIT_FILE_SCOPE_MAX); + + r = verify_root_dir(scope, &root_dir); + if (r < 0) + return r; + + r = get_config_path(scope, runtime, root_dir, &config_path); + if (r < 0) + return r; + + STRV_FOREACH(i, files) { + _cleanup_free_ char *path = NULL; + + if (!unit_name_is_valid(*i, UNIT_NAME_ANY)) + return -EINVAL; + + path = path_make_absolute(*i, config_path); + if (!path) + return -ENOMEM; + + r = null_or_empty_path(path); + if (r == -ENOENT) + continue; + if (r < 0) + return r; + if (r == 0) + continue; + + if (!GREEDY_REALLOC0(todo, n_allocated, n_todo + 2)) + return -ENOMEM; + + todo[n_todo++] = *i; + } + + strv_uniq(todo); + + r = 0; + STRV_FOREACH(i, todo) { + _cleanup_free_ char *path = NULL; + + path = path_make_absolute(*i, config_path); + if (!path) + return -ENOMEM; + + if (unlink(path) < 0) { + if (errno != -ENOENT && r >= 0) + r = -errno; + } else { + q = mark_symlink_for_removal(&remove_symlinks_to, path); + if (q < 0) + return q; + + unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, path, NULL); + } + } + + q = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes); + if (r >= 0) + r = q; + + return r; +} + +int unit_file_link( UnitFileScope scope, bool runtime, const char *root_dir, char **files, - char *target, - UnitDependency dep, bool force, UnitFileChange **changes, unsigned *n_changes) { _cleanup_lookup_paths_free_ LookupPaths paths = {}; - _cleanup_(install_context_done) InstallContext c = {}; _cleanup_free_ char *config_path = NULL; + _cleanup_free_ char **todo = NULL; + size_t n_todo = 0, n_allocated = 0; char **i; - int r; - UnitFileInstallInfo *info; + int r, q; assert(scope >= 0); assert(scope < _UNIT_FILE_SCOPE_MAX); + r = verify_root_dir(scope, &root_dir); + if (r < 0) + return r; + r = lookup_paths_init_from_scope(&paths, scope, root_dir); if (r < 0) return r; @@ -1533,55 +1642,135 @@ int unit_file_add_dependency( return r; STRV_FOREACH(i, files) { - UnitFileState state; + _cleanup_free_ char *full = NULL; + struct stat st; + char *fn; - state = unit_file_get_state(scope, root_dir, *i); - if (state < 0) - return log_error_errno(state, "Failed to get unit file state for %s: %m", *i); + if (!path_is_absolute(*i)) + return -EINVAL; - if (state == UNIT_FILE_MASKED || state == UNIT_FILE_MASKED_RUNTIME) { - log_error("Failed to enable unit: Unit %s is masked", *i); - return -EOPNOTSUPP; - } + fn = basename(*i); + if (!unit_name_is_valid(fn, UNIT_NAME_ANY)) + return -EINVAL; - r = install_info_add_auto(&c, *i); - if (r < 0) - return r; + full = prefix_root(root_dir, *i); + if (!full) + return -ENOMEM; + + if (lstat(full, &st) < 0) + return -errno; + if (S_ISLNK(st.st_mode)) + return -ELOOP; + if (S_ISDIR(st.st_mode)) + return -EISDIR; + if (!S_ISREG(st.st_mode)) + return -ENOTTY; + + q = in_search_path(*i, paths.unit_path); + if (q < 0) + return q; + if (q > 0) + continue; + + if (!GREEDY_REALLOC0(todo, n_allocated, n_todo + 2)) + return -ENOMEM; + + todo[n_todo++] = *i; } - if (!ordered_hashmap_isempty(c.will_install)) { - r = ordered_hashmap_ensure_allocated(&c.have_installed, &string_hash_ops); - if (r < 0) - return r; + strv_uniq(todo); - r = ordered_hashmap_reserve(c.have_installed, ordered_hashmap_size(c.will_install)); - if (r < 0) - return r; + r = 0; + STRV_FOREACH(i, todo) { + _cleanup_free_ char *path = NULL; + + path = path_make_absolute(basename(*i), config_path); + if (!path) + return -ENOMEM; + + q = create_symlink(*i, path, force, changes, n_changes); + if (q < 0 && r >= 0) + r = q; } - while ((info = ordered_hashmap_first(c.will_install))) { - assert_se(ordered_hashmap_move_one(c.have_installed, c.will_install, info->name) == 0); + return r; +} + +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) { + + _cleanup_lookup_paths_free_ LookupPaths paths = {}; + _cleanup_(install_context_done) InstallContext c = {}; + _cleanup_free_ char *config_path = NULL; + UnitFileInstallInfo *i, *target_info; + char **f; + int r; + + assert(scope >= 0); + assert(scope < _UNIT_FILE_SCOPE_MAX); + assert(target); + + if (!IN_SET(dep, UNIT_WANTS, UNIT_REQUIRES)) + return -EINVAL; - r = unit_file_search(&c, info, &paths, root_dir, false, false, NULL); + if (!unit_name_is_valid(target, UNIT_NAME_ANY)) + return -EINVAL; + + r = verify_root_dir(scope, &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; + + r = install_info_discover(scope, &c, root_dir, &paths, target, SEARCH_FOLLOW_CONFIG_SYMLINKS, &target_info); + 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); if (r < 0) return r; + if (i->type == UNIT_FILE_TYPE_MASKED) + return -ESHUTDOWN; + + assert(i->type == UNIT_FILE_TYPE_REGULAR); + + /* We didn't actually load anything from the unit + * file, but instead just add in our new symlink to + * create. */ if (dep == UNIT_WANTS) - r = strv_extend(&info->wanted_by, target); - else if (dep == UNIT_REQUIRES) - r = strv_extend(&info->required_by, target); + l = &i->wanted_by; else - r = -EINVAL; + l = &i->required_by; - if (r < 0) - return r; - - r = install_info_apply(info, &paths, config_path, root_dir, force, changes, n_changes); - if (r < 0) - return r; + strv_free(*l); + *l = strv_new(target_info->name, NULL); + if (!*l) + return -ENOMEM; } - return 0; + return install_context_apply(scope, &c, &paths, config_path, root_dir, force, SEARCH_FOLLOW_CONFIG_SYMLINKS, changes, n_changes); } int unit_file_enable( @@ -1595,13 +1784,18 @@ int unit_file_enable( _cleanup_lookup_paths_free_ LookupPaths paths = {}; _cleanup_(install_context_done) InstallContext c = {}; - char **i; _cleanup_free_ char *config_path = NULL; + UnitFileInstallInfo *i; + char **f; int r; assert(scope >= 0); assert(scope < _UNIT_FILE_SCOPE_MAX); + r = verify_root_dir(scope, &root_dir); + if (r < 0) + return r; + r = lookup_paths_init_from_scope(&paths, scope, root_dir); if (r < 0) return r; @@ -1610,29 +1804,22 @@ int unit_file_enable( if (r < 0) return r; - STRV_FOREACH(i, files) { - UnitFileState state; - - /* We only want to know if this unit is masked, so we ignore - * errors from unit_file_get_state, deferring other checks. - * This allows templated units to be enabled on the fly. */ - state = unit_file_get_state(scope, root_dir, *i); - if (state == UNIT_FILE_MASKED || state == UNIT_FILE_MASKED_RUNTIME) { - log_error("Failed to enable unit: Unit %s is masked", *i); - return -EOPNOTSUPP; - } - - r = install_info_add_auto(&c, *i); + STRV_FOREACH(f, files) { + r = install_info_discover(scope, &c, root_dir, &paths, *f, SEARCH_LOAD, &i); if (r < 0) return r; + if (i->type == UNIT_FILE_TYPE_MASKED) + return -ESHUTDOWN; + + assert(i->type == UNIT_FILE_TYPE_REGULAR); } /* This will return the number of symlink rules that were - supposed to be created, not the ones actually created. This is - useful to determine whether the passed files had any - installation data at all. */ + supposed to be created, not the ones actually created. This + is useful to determine whether the passed files had any + installation data at all. */ - return install_context_apply(&c, &paths, config_path, root_dir, force, changes, n_changes); + return install_context_apply(scope, &c, &paths, config_path, root_dir, force, SEARCH_LOAD, changes, n_changes); } int unit_file_disable( @@ -1645,14 +1832,18 @@ int unit_file_disable( _cleanup_lookup_paths_free_ LookupPaths paths = {}; _cleanup_(install_context_done) InstallContext c = {}; - char **i; _cleanup_free_ char *config_path = NULL; _cleanup_set_free_free_ Set *remove_symlinks_to = NULL; - int r, q; + char **i; + int r; assert(scope >= 0); assert(scope < _UNIT_FILE_SCOPE_MAX); + r = verify_root_dir(scope, &root_dir); + if (r < 0) + return r; + r = lookup_paths_init_from_scope(&paths, scope, root_dir); if (r < 0) return r; @@ -1662,18 +1853,19 @@ int unit_file_disable( return r; STRV_FOREACH(i, files) { - r = install_info_add_auto(&c, *i); + if (!unit_name_is_valid(*i, UNIT_NAME_ANY)) + return -EINVAL; + + r = install_info_add(&c, *i, NULL, NULL); if (r < 0) return r; } - r = install_context_mark_for_removal(&c, &paths, &remove_symlinks_to, config_path, root_dir); - - q = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes, files); - if (r >= 0) - r = q; + r = install_context_mark_for_removal(scope, &c, &paths, &remove_symlinks_to, config_path, root_dir); + if (r < 0) + return r; - return r; + return remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes); } int unit_file_reenable( @@ -1684,21 +1876,30 @@ int unit_file_reenable( bool force, UnitFileChange **changes, unsigned *n_changes) { + + char **n; int r; + size_t l, i; + + /* First, we invoke the disable command with only the basename... */ + l = strv_length(files); + n = newa(char*, l+1); + for (i = 0; i < l; i++) + n[i] = basename(files[i]); + n[i] = NULL; - r = unit_file_disable(scope, runtime, root_dir, files, - changes, n_changes); + r = unit_file_disable(scope, runtime, root_dir, n, changes, n_changes); if (r < 0) return r; - return unit_file_enable(scope, runtime, root_dir, files, force, - changes, n_changes); + /* But the enable command with the full name */ + return unit_file_enable(scope, runtime, root_dir, files, force, changes, n_changes); } int unit_file_set_default( UnitFileScope scope, const char *root_dir, - const char *file, + const char *name, bool force, UnitFileChange **changes, unsigned *n_changes) { @@ -1706,42 +1907,40 @@ int unit_file_set_default( _cleanup_lookup_paths_free_ LookupPaths paths = {}; _cleanup_(install_context_done) InstallContext c = {}; _cleanup_free_ char *config_path = NULL; - char *path; + UnitFileInstallInfo *i; + const char *path; int r; - UnitFileInstallInfo *i = NULL; assert(scope >= 0); assert(scope < _UNIT_FILE_SCOPE_MAX); - assert(file); + assert(name); - if (unit_name_to_type(file) != UNIT_TARGET) + if (unit_name_to_type(name) != UNIT_TARGET) + return -EINVAL; + if (streq(name, SPECIAL_DEFAULT_TARGET)) return -EINVAL; - r = lookup_paths_init_from_scope(&paths, scope, root_dir); + r = verify_root_dir(scope, &root_dir); if (r < 0) return r; - r = get_config_path(scope, false, root_dir, &config_path); + r = lookup_paths_init_from_scope(&paths, scope, root_dir); if (r < 0) return r; - r = install_info_add_auto(&c, file); + r = get_config_path(scope, false, root_dir, &config_path); if (r < 0) return r; - assert_se(i = ordered_hashmap_first(c.will_install)); - - r = unit_file_search(&c, i, &paths, root_dir, false, true, NULL); + 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); - r = create_symlink(i->path, path, force, changes, n_changes); - if (r < 0) - return r; - - return 0; + return create_symlink(i->path, path, force, changes, n_changes); } int unit_file_get_default( @@ -1750,126 +1949,101 @@ int unit_file_get_default( char **name) { _cleanup_lookup_paths_free_ LookupPaths paths = {}; - char **p; + _cleanup_(install_context_done) InstallContext c = {}; + UnitFileInstallInfo *i; + char *n; int r; assert(scope >= 0); assert(scope < _UNIT_FILE_SCOPE_MAX); assert(name); - r = lookup_paths_init_from_scope(&paths, scope, root_dir); + r = verify_root_dir(scope, &root_dir); if (r < 0) return r; - STRV_FOREACH(p, paths.unit_path) { - _cleanup_free_ char *path = NULL, *tmp = NULL; - char *n; - - path = path_join(root_dir, *p, SPECIAL_DEFAULT_TARGET); - if (!path) - return -ENOMEM; - - r = readlink_malloc(path, &tmp); - if (r == -ENOENT) - continue; - else if (r == -EINVAL) - /* not a symlink */ - n = strdup(SPECIAL_DEFAULT_TARGET); - else if (r < 0) - return r; - else - n = strdup(basename(tmp)); + r = lookup_paths_init_from_scope(&paths, scope, root_dir); + if (r < 0) + return r; - if (!n) - return -ENOMEM; + r = install_info_discover(scope, &c, root_dir, &paths, SPECIAL_DEFAULT_TARGET, SEARCH_FOLLOW_CONFIG_SYMLINKS, &i); + if (r < 0) + return r; + if (i->type == UNIT_FILE_TYPE_MASKED) + return -ESHUTDOWN; - *name = n; - return 0; - } + n = strdup(i->name); + if (!n) + return -ENOMEM; - return -ENOENT; + *name = n; + return 0; } -UnitFileState unit_file_lookup_state( +int unit_file_lookup_state( UnitFileScope scope, const char *root_dir, const LookupPaths *paths, - const char *name) { + const char *name, + UnitFileState *ret) { - UnitFileState state = _UNIT_FILE_STATE_INVALID; - char **i; - _cleanup_free_ char *path = NULL; - int r = 0; + _cleanup_(install_context_done) InstallContext c = {}; + UnitFileInstallInfo *i; + UnitFileState state; + int r; assert(paths); + assert(name); if (!unit_name_is_valid(name, UNIT_NAME_ANY)) return -EINVAL; - STRV_FOREACH(i, paths->unit_path) { - struct stat st; - char *partial; - bool also = false; + r = verify_root_dir(scope, &root_dir); + if (r < 0) + return r; - free(path); - path = path_join(root_dir, *i, name); - if (!path) - return -ENOMEM; + r = install_info_discover(scope, &c, root_dir, paths, name, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, &i); + if (r < 0) + return r; - if (root_dir) - partial = path + strlen(root_dir); - else - partial = path; - - /* - * Search for a unit file in our default paths, to - * be sure, that there are no broken symlinks. - */ - if (lstat(path, &st) < 0) { - r = -errno; - if (errno != ENOENT) - return r; + /* Shortcut things, if the caller just wants to know if this unit exists. */ + if (!ret) + return 0; - if (!unit_name_is_valid(name, UNIT_NAME_INSTANCE)) - continue; - } else { - if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) - return -ENOENT; + switch (i->type) { - r = null_or_empty_path(path); - if (r < 0 && r != -ENOENT) - return r; - else if (r > 0) { - state = path_startswith(*i, "/run") ? UNIT_FILE_MASKED_RUNTIME : UNIT_FILE_MASKED; - return state; - } - } + case UNIT_FILE_TYPE_MASKED: + state = path_startswith(i->path, "/run") ? UNIT_FILE_MASKED_RUNTIME : UNIT_FILE_MASKED; + break; - r = find_symlinks_in_scope(scope, root_dir, name, &state); + case UNIT_FILE_TYPE_REGULAR: + r = find_symlinks_in_scope(scope, root_dir, i->name, &state); if (r < 0) return r; - else if (r > 0) - return state; - - r = unit_file_can_install(paths, root_dir, partial, true, &also); - if (r < 0 && errno != ENOENT) - return r; - else if (r > 0) - return UNIT_FILE_DISABLED; - else if (r == 0) { - if (also) - return UNIT_FILE_INDIRECT; - return UNIT_FILE_STATIC; + if (r == 0) { + if (UNIT_FILE_INSTALL_INFO_HAS_RULES(i)) + state = UNIT_FILE_DISABLED; + else if (UNIT_FILE_INSTALL_INFO_HAS_ALSO(i)) + state = UNIT_FILE_INDIRECT; + else + state = UNIT_FILE_STATIC; } + + break; + + default: + assert_not_reached("Unexpect unit file type."); } - return r < 0 ? r : state; + *ret = state; + return 0; } -UnitFileState unit_file_get_state( +int unit_file_get_state( UnitFileScope scope, const char *root_dir, - const char *name) { + const char *name, + UnitFileState *ret) { _cleanup_lookup_paths_free_ LookupPaths paths = {}; int r; @@ -1878,14 +2052,15 @@ UnitFileState unit_file_get_state( assert(scope < _UNIT_FILE_SCOPE_MAX); assert(name); - if (root_dir && scope != UNIT_FILE_SYSTEM) - return -EINVAL; + r = verify_root_dir(scope, &root_dir); + if (r < 0) + return r; r = lookup_paths_init_from_scope(&paths, scope, root_dir); if (r < 0) return r; - return unit_file_lookup_state(scope, root_dir, &paths, name); + return unit_file_lookup_state(scope, root_dir, &paths, name, ret); } int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char *name) { @@ -1897,6 +2072,13 @@ 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; + if (scope == UNIT_FILE_SYSTEM) r = conf_files_list(&files, ".preset", root_dir, "/etc/systemd/system-preset", @@ -1913,13 +2095,14 @@ int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char "/usr/lib/systemd/user-preset", NULL); else - return 1; + return 1; /* Default is "enable" */ if (r < 0) return r; STRV_FOREACH(p, files) { _cleanup_fclose_ FILE *f; + char line[LINE_MAX]; f = fopen(*p, "re"); if (!f) { @@ -1929,39 +2112,38 @@ int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char return -errno; } - for (;;) { - char line[LINE_MAX], *l; - - if (!fgets(line, sizeof(line), f)) - break; + FOREACH_LINE(line, f, return -errno) { + const char *parameter; + char *l; l = strstrip(line); - if (!*l) - continue; - if (strchr(COMMENTS "\n", *l)) + if (isempty(l)) + continue; + if (strchr(COMMENTS, *l)) continue; - if (first_word(l, "enable")) { - l += 6; - l += strspn(l, WHITESPACE); - - if (fnmatch(l, name, FNM_NOESCAPE) == 0) { + parameter = first_word(l, "enable"); + if (parameter) { + if (fnmatch(parameter, name, FNM_NOESCAPE) == 0) { log_debug("Preset file says enable %s.", name); return 1; } - } else if (first_word(l, "disable")) { - l += 7; - l += strspn(l, WHITESPACE); + continue; + } - if (fnmatch(l, name, FNM_NOESCAPE) == 0) { + parameter = first_word(l, "disable"); + if (parameter) { + if (fnmatch(parameter, name, FNM_NOESCAPE) == 0) { log_debug("Preset file says disable %s.", name); return 0; } - } else - log_debug("Couldn't parse line '%s'", l); + continue; + } + + log_debug("Couldn't parse line '%s'", l); } } @@ -1970,6 +2152,86 @@ int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char return 1; } +static int execute_preset( + UnitFileScope scope, + InstallContext *plus, + InstallContext *minus, + const LookupPaths *paths, + const char *config_path, + const char *root_dir, + char **files, + UnitFilePresetMode mode, + bool force, + UnitFileChange **changes, + unsigned *n_changes) { + + int r; + + assert(plus); + assert(minus); + assert(paths); + assert(config_path); + + 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); + if (r < 0) + return r; + + r = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes); + } else + r = 0; + + if (mode != UNIT_FILE_PRESET_DISABLE_ONLY) { + 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); + if (r >= 0) { + if (q < 0) + r = q; + else + r+= q; + } + } + + return r; +} + +static int preset_prepare_one( + UnitFileScope scope, + InstallContext *plus, + InstallContext *minus, + LookupPaths *paths, + const char *root_dir, + UnitFilePresetMode mode, + const char *name) { + + UnitFileInstallInfo *i; + int r; + + if (install_info_find(plus, name) || + install_info_find(minus, name)) + return 0; + + r = unit_file_query_preset(scope, 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); + if (r < 0) + return r; + + if (i->type == UNIT_FILE_TYPE_MASKED) + return -ESHUTDOWN; + } else + r = install_info_discover(scope, minus, root_dir, paths, name, SEARCH_FOLLOW_CONFIG_SYMLINKS, &i); + + return r; +} + int unit_file_preset( UnitFileScope scope, bool runtime, @@ -1984,12 +2246,16 @@ int unit_file_preset( _cleanup_lookup_paths_free_ LookupPaths paths = {}; _cleanup_free_ char *config_path = NULL; char **i; - int r, q; + int r; assert(scope >= 0); 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); if (r < 0) return r; @@ -1999,44 +2265,15 @@ int unit_file_preset( return r; STRV_FOREACH(i, files) { - if (!unit_name_is_valid(*i, UNIT_NAME_ANY)) return -EINVAL; - r = unit_file_query_preset(scope, root_dir, *i); + r = preset_prepare_one(scope, &plus, &minus, &paths, root_dir, mode, *i); if (r < 0) return r; - - if (r && mode != UNIT_FILE_PRESET_DISABLE_ONLY) - r = install_info_add_auto(&plus, *i); - else if (!r && mode != UNIT_FILE_PRESET_ENABLE_ONLY) - r = install_info_add_auto(&minus, *i); - else - r = 0; - if (r < 0) - return r; - } - - r = 0; - - if (mode != UNIT_FILE_PRESET_ENABLE_ONLY) { - _cleanup_set_free_free_ Set *remove_symlinks_to = NULL; - - r = install_context_mark_for_removal(&minus, &paths, &remove_symlinks_to, config_path, root_dir); - - q = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes, files); - if (r == 0) - r = q; } - if (mode != UNIT_FILE_PRESET_DISABLE_ONLY) { - /* Returns number of symlinks that where supposed to be installed. */ - q = install_context_apply(&plus, &paths, config_path, root_dir, force, changes, n_changes); - if (r == 0) - r = q; - } - - return r; + return execute_preset(scope, &plus, &minus, &paths, config_path, root_dir, files, mode, force, changes, n_changes); } int unit_file_preset_all( @@ -2052,12 +2289,16 @@ int unit_file_preset_all( _cleanup_lookup_paths_free_ LookupPaths paths = {}; _cleanup_free_ char *config_path = NULL; char **i; - int r, q; + int r; assert(scope >= 0); 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); if (r < 0) return r; @@ -2069,6 +2310,7 @@ int unit_file_preset_all( STRV_FOREACH(i, paths.unit_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) @@ -2082,62 +2324,23 @@ int unit_file_preset_all( return -errno; } - for (;;) { - struct dirent *de; - - errno = 0; - de = readdir(d); - if (!de && errno != 0) - return -errno; - - if (!de) - break; - - if (hidden_file(de->d_name)) - continue; + FOREACH_DIRENT(de, d, return -errno) { if (!unit_name_is_valid(de->d_name, UNIT_NAME_ANY)) continue; dirent_ensure_type(d, de); - if (de->d_type != DT_REG) + if (!IN_SET(de->d_type, DT_LNK, DT_REG)) continue; - r = unit_file_query_preset(scope, root_dir, de->d_name); - if (r < 0) - return r; - - if (r && mode != UNIT_FILE_PRESET_DISABLE_ONLY) - r = install_info_add_auto(&plus, de->d_name); - else if (!r && mode != UNIT_FILE_PRESET_ENABLE_ONLY) - r = install_info_add_auto(&minus, de->d_name); - else - r = 0; + r = preset_prepare_one(scope, &plus, &minus, &paths, root_dir, mode, de->d_name); if (r < 0) return r; } } - r = 0; - - if (mode != UNIT_FILE_PRESET_ENABLE_ONLY) { - _cleanup_set_free_free_ Set *remove_symlinks_to = NULL; - - r = install_context_mark_for_removal(&minus, &paths, &remove_symlinks_to, config_path, root_dir); - - q = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes, NULL); - if (r == 0) - r = q; - } - - if (mode != UNIT_FILE_PRESET_DISABLE_ONLY) { - q = install_context_apply(&plus, &paths, config_path, root_dir, force, changes, n_changes); - if (r == 0) - r = q; - } - - return r; + return execute_preset(scope, &plus, &minus, &paths, config_path, root_dir, NULL, mode, force, changes, n_changes); } static void unit_file_list_free_one(UnitFileList *f) { @@ -2148,6 +2351,15 @@ static void unit_file_list_free_one(UnitFileList *f) { free(f); } +Hashmap* unit_file_list_free(Hashmap *h) { + UnitFileList *i; + + while ((i = hashmap_steal_first(h))) + unit_file_list_free_one(i); + + return hashmap_free(h); +} + DEFINE_TRIVIAL_CLEANUP_FUNC(UnitFileList*, unit_file_list_free_one); int unit_file_get_list( @@ -2163,14 +2375,9 @@ int unit_file_get_list( assert(scope < _UNIT_FILE_SCOPE_MAX); assert(h); - if (root_dir && scope != UNIT_FILE_SYSTEM) - return -EINVAL; - - if (root_dir) { - r = access(root_dir, F_OK); - if (r < 0) - return -errno; - } + r = verify_root_dir(scope, &root_dir); + if (r < 0) + return r; r = lookup_paths_init_from_scope(&paths, scope, root_dir); if (r < 0) @@ -2179,6 +2386,7 @@ int unit_file_get_list( STRV_FOREACH(i, paths.unit_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) @@ -2192,22 +2400,8 @@ int unit_file_get_list( return -errno; } - for (;;) { + FOREACH_DIRENT(de, d, return -errno) { _cleanup_(unit_file_list_free_onep) UnitFileList *f = NULL; - struct dirent *de; - _cleanup_free_ char *path = NULL; - bool also = false; - - errno = 0; - de = readdir(d); - if (!de && errno != 0) - return -errno; - - if (!de) - break; - - if (hidden_file(de->d_name)) - continue; if (!unit_name_is_valid(de->d_name, UNIT_NAME_ANY)) continue; @@ -2228,44 +2422,14 @@ int unit_file_get_list( if (!f->path) return -ENOMEM; - r = null_or_empty_path(f->path); - if (r < 0 && r != -ENOENT) - return r; - else if (r > 0) { - f->state = - path_startswith(*i, "/run") ? - UNIT_FILE_MASKED_RUNTIME : UNIT_FILE_MASKED; - goto found; - } - - r = find_symlinks_in_scope(scope, root_dir, de->d_name, &f->state); + r = unit_file_lookup_state(scope, root_dir, &paths, basename(f->path), &f->state); if (r < 0) - return r; - else if (r > 0) { - f->state = UNIT_FILE_ENABLED; - goto found; - } - - path = path_make_absolute(de->d_name, *i); - if (!path) - return -ENOMEM; + f->state = UNIT_FILE_BAD; - r = unit_file_can_install(&paths, root_dir, path, true, &also); - if (r == -EINVAL || /* Invalid setting? */ - r == -EBADMSG || /* Invalid format? */ - r == -ENOENT /* Included file not found? */) - f->state = UNIT_FILE_INVALID; - else if (r < 0) - return r; - else if (r > 0) - f->state = UNIT_FILE_DISABLED; - else - f->state = also ? UNIT_FILE_INDIRECT : UNIT_FILE_STATIC; - - found: r = hashmap_put(h, basename(f->path), f); if (r < 0) return r; + f = NULL; /* prevent cleanup */ } } @@ -2283,7 +2447,7 @@ 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_INVALID] = "invalid", + [UNIT_FILE_BAD] = "bad", }; DEFINE_STRING_TABLE_LOOKUP(unit_file_state, UnitFileState); diff --git a/src/shared/install.h b/src/shared/install.h index a9d77dd91b..45a417df92 100644 --- a/src/shared/install.h +++ b/src/shared/install.h @@ -25,13 +25,15 @@ typedef enum UnitFileScope UnitFileScope; typedef enum UnitFileState UnitFileState; typedef enum UnitFilePresetMode UnitFilePresetMode; typedef enum UnitFileChangeType UnitFileChangeType; +typedef enum UnitFileType UnitFileType; typedef struct UnitFileChange UnitFileChange; typedef struct UnitFileList UnitFileList; typedef struct UnitFileInstallInfo UnitFileInstallInfo; #include "hashmap.h" -#include "unit-name.h" #include "path-lookup.h" +#include "strv.h" +#include "unit-name.h" enum UnitFileScope { UNIT_FILE_SYSTEM, @@ -51,7 +53,7 @@ enum UnitFileState { UNIT_FILE_STATIC, UNIT_FILE_DISABLED, UNIT_FILE_INDIRECT, - UNIT_FILE_INVALID, + UNIT_FILE_BAD, _UNIT_FILE_STATE_MAX, _UNIT_FILE_STATE_INVALID = -1 }; @@ -82,10 +84,17 @@ struct UnitFileList { UnitFileState state; }; +enum UnitFileType { + UNIT_FILE_TYPE_REGULAR, + UNIT_FILE_TYPE_SYMLINK, + UNIT_FILE_TYPE_MASKED, + _UNIT_FILE_TYPE_MAX, + _UNIT_FILE_TYPE_INVALID = -1, +}; + struct UnitFileInstallInfo { char *name; char *path; - char *user; char **aliases; char **wanted_by; @@ -93,8 +102,26 @@ struct UnitFileInstallInfo { char **also; char *default_instance; + + UnitFileType type; + + char *symlink_target; }; +static inline bool UNIT_FILE_INSTALL_INFO_HAS_RULES(UnitFileInstallInfo *i) { + assert(i); + + return !strv_isempty(i->aliases) || + !strv_isempty(i->wanted_by) || + !strv_isempty(i->required_by); +} + +static inline bool UNIT_FILE_INSTALL_INFO_HAS_ALSO(UnitFileInstallInfo *i) { + assert(i); + + return !strv_isempty(i->also); +} + int unit_file_enable(UnitFileScope scope, bool runtime, const char *root_dir, char **files, bool force, UnitFileChange **changes, unsigned *n_changes); int unit_file_disable(UnitFileScope scope, bool runtime, const char *root_dir, char **files, UnitFileChange **changes, unsigned *n_changes); int unit_file_reenable(UnitFileScope scope, bool runtime, const char *root_dir, char **files, bool force, UnitFileChange **changes, unsigned *n_changes); @@ -105,21 +132,14 @@ int unit_file_mask(UnitFileScope scope, bool runtime, const char *root_dir, char int unit_file_unmask(UnitFileScope scope, bool runtime, const char *root_dir, char **files, UnitFileChange **changes, unsigned *n_changes); int unit_file_set_default(UnitFileScope scope, const char *root_dir, const char *file, bool force, UnitFileChange **changes, unsigned *n_changes); int unit_file_get_default(UnitFileScope scope, const char *root_dir, char **name); -int unit_file_add_dependency(UnitFileScope scope, bool runtime, const char *root_dir, char **files, char *target, UnitDependency dep, bool force, UnitFileChange **changes, unsigned *n_changes); - -UnitFileState unit_file_lookup_state( - UnitFileScope scope, - const char *root_dir, - const LookupPaths *paths, - const char *name); -UnitFileState unit_file_get_state( - UnitFileScope scope, - const char *root_dir, - const char *filename); +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_get_list(UnitFileScope scope, const char *root_dir, Hashmap *h); +Hashmap* unit_file_list_free(Hashmap *h); -void unit_file_list_free(Hashmap *h); int unit_file_changes_add(UnitFileChange **changes, unsigned *n_changes, UnitFileChangeType type, const char *path, const char *source); void unit_file_changes_free(UnitFileChange *changes, unsigned n_changes); diff --git a/src/shared/logs-show.h b/src/shared/logs-show.h index 569e1faa55..98927bbc59 100644 --- a/src/shared/logs-show.h +++ b/src/shared/logs-show.h @@ -26,8 +26,8 @@ #include "sd-journal.h" -#include "util.h" #include "output-mode.h" +#include "util.h" int output_journal( FILE *f, diff --git a/src/shared/machine-image.h b/src/shared/machine-image.h index f041600fbf..038db7453c 100644 --- a/src/shared/machine-image.h +++ b/src/shared/machine-image.h @@ -21,9 +21,9 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include "time-util.h" -#include "lockfile-util.h" #include "hashmap.h" +#include "lockfile-util.h" +#include "time-util.h" typedef enum ImageType { IMAGE_DIRECTORY, diff --git a/src/shared/nss-util.h b/src/shared/nss-util.h index 3657aa5d9c..a7b51a91da 100644 --- a/src/shared/nss-util.h +++ b/src/shared/nss-util.h @@ -21,11 +21,11 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <nss.h> +#include <grp.h> #include <netdb.h> -#include <resolv.h> +#include <nss.h> #include <pwd.h> -#include <grp.h> +#include <resolv.h> #define NSS_GETHOSTBYNAME_PROTOTYPES(module) \ diff --git a/src/shared/path-lookup.c b/src/shared/path-lookup.c index d71f379e76..4a82bd18cd 100644 --- a/src/shared/path-lookup.c +++ b/src/shared/path-lookup.c @@ -19,18 +19,18 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <stdlib.h> +#include <errno.h> #include <stdio.h> +#include <stdlib.h> #include <string.h> -#include <errno.h> #include "alloc-util.h" -#include "util.h" -#include "strv.h" -#include "path-util.h" #include "install.h" -#include "string-util.h" #include "path-lookup.h" +#include "path-util.h" +#include "string-util.h" +#include "strv.h" +#include "util.h" int user_config_home(char **config_home) { const char *e; diff --git a/src/shared/ptyfwd.c b/src/shared/ptyfwd.c index 63e81f4894..2666b8f7e2 100644 --- a/src/shared/ptyfwd.c +++ b/src/shared/ptyfwd.c @@ -19,9 +19,9 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <limits.h> #include <sys/epoll.h> #include <sys/ioctl.h> -#include <limits.h> #include <termios.h> #include "alloc-util.h" diff --git a/src/shared/seccomp-util.c b/src/shared/seccomp-util.c index c518cf83ec..09baf51661 100644 --- a/src/shared/seccomp-util.c +++ b/src/shared/seccomp-util.c @@ -21,9 +21,9 @@ #include <seccomp.h> +#include "seccomp-util.h" #include "string-util.h" #include "util.h" -#include "seccomp-util.h" const char* seccomp_arch_to_string(uint32_t c) { diff --git a/src/shared/spawn-ask-password-agent.c b/src/shared/spawn-ask-password-agent.c index 29db855c67..3fcea61873 100644 --- a/src/shared/spawn-ask-password-agent.c +++ b/src/shared/spawn-ask-password-agent.c @@ -25,8 +25,8 @@ #include "log.h" #include "process-util.h" -#include "util.h" #include "spawn-ask-password-agent.h" +#include "util.h" static pid_t agent_pid = 0; diff --git a/src/shared/spawn-polkit-agent.c b/src/shared/spawn-polkit-agent.c index ec6e5a8312..8ea6cb830b 100644 --- a/src/shared/spawn-polkit-agent.c +++ b/src/shared/spawn-polkit-agent.c @@ -19,11 +19,11 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <stdlib.h> -#include <unistd.h> -#include <signal.h> #include <errno.h> #include <poll.h> +#include <signal.h> +#include <stdlib.h> +#include <unistd.h> #include "fd-util.h" #include "io-util.h" diff --git a/src/shared/sysctl-util.c b/src/shared/sysctl-util.c index 21cb82ea1c..70caa542e7 100644 --- a/src/shared/sysctl-util.c +++ b/src/shared/sysctl-util.c @@ -30,8 +30,8 @@ #include "fileio.h" #include "log.h" #include "string-util.h" -#include "util.h" #include "sysctl-util.h" +#include "util.h" char *sysctl_normalize(char *s) { char *n; diff --git a/src/shared/watchdog.c b/src/shared/watchdog.c index d58f9873d5..7131e94cdb 100644 --- a/src/shared/watchdog.c +++ b/src/shared/watchdog.c @@ -19,15 +19,15 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <sys/ioctl.h> #include <errno.h> #include <fcntl.h> +#include <sys/ioctl.h> #include <unistd.h> #include <linux/watchdog.h> -#include "watchdog.h" -#include "log.h" #include "fd-util.h" +#include "log.h" +#include "watchdog.h" static int watchdog_fd = -1; static usec_t watchdog_timeout = USEC_INFINITY; diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 70871cf3e6..f478d809c2 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -1335,7 +1335,7 @@ static void output_unit_file_list(const UnitFileList *units, unsigned c) { UNIT_FILE_MASKED, UNIT_FILE_MASKED_RUNTIME, UNIT_FILE_DISABLED, - UNIT_FILE_INVALID)) { + UNIT_FILE_BAD)) { on = ansi_highlight_red(); off = ansi_normal(); } else if (u->state == UNIT_FILE_ENABLED) { @@ -1503,16 +1503,12 @@ static int list_dependencies_get_dependencies(sd_bus *bus, const char *name, cha static const char *dependencies[_DEPENDENCY_MAX] = { [DEPENDENCY_FORWARD] = "Requires\0" - "RequiresOverridable\0" "Requisite\0" - "RequisiteOverridable\0" "Wants\0" "ConsistsOf\0" "BindsTo\0", [DEPENDENCY_REVERSE] = "RequiredBy\0" - "RequiredByOverridable\0" "RequisiteOf\0" - "RequisiteOfOverridable\0" "WantedBy\0" "PartOf\0" "BoundBy\0", @@ -2186,7 +2182,7 @@ static int list_jobs(int argc, char *argv[], void *userdata) { return bus_log_parse_error(r); output_jobs_list(jobs, c, skipped); - return r; + return 0; } static int cancel_job(int argc, char *argv[], void *userdata) { @@ -3364,6 +3360,7 @@ typedef struct UnitStatusInfo { usec_t inactive_enter_timestamp; bool need_daemon_reload; + bool transient; /* Service */ pid_t main_pid; @@ -3463,7 +3460,7 @@ static void print_status_info( path = i->source_path ? i->source_path : i->fragment_path; - if (i->load_error) + if (i->load_error != 0) printf(" Loaded: %s%s%s (Reason: %s)\n", on, strna(i->load_state), off, i->load_error); else if (path && !isempty(i->unit_file_state) && !isempty(i->unit_file_preset)) @@ -3479,6 +3476,9 @@ static void print_status_info( printf(" Loaded: %s%s%s\n", on, strna(i->load_state), off); + if (i->transient) + printf("Transient: yes\n"); + if (!strv_isempty(i->dropin_paths)) { _cleanup_free_ char *dir = NULL; bool last = false; @@ -3843,6 +3843,8 @@ static int status_property(const char *name, sd_bus_message *m, UnitStatusInfo * i->condition_result = b; else if (streq(name, "AssertResult")) i->assert_result = b; + else if (streq(name, "Transient")) + i->transient = b; break; } @@ -4650,8 +4652,7 @@ static int show(int argc, char *argv[], void *userdata) { return -EINVAL; } - if (show_properties) - pager_open_if_enabled(); + pager_open_if_enabled(); if (show_status) /* Increase max number of open files to 16K if we can, we @@ -4905,102 +4906,6 @@ static int set_property(int argc, char *argv[], void *userdata) { return 0; } -static int snapshot(int argc, char *argv[], void *userdata) { - _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; - _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; - _cleanup_free_ char *n = NULL, *id = NULL; - const char *path; - sd_bus *bus; - int r; - - polkit_agent_open_if_enabled(); - - if (argc > 1) { - r = unit_name_mangle_with_suffix(argv[1], UNIT_NAME_NOGLOB, ".snapshot", &n); - if (r < 0) - return log_error_errno(r, "Failed to generate unit name: %m"); - } else { - n = strdup(""); - if (!n) - return log_oom(); - } - - r = acquire_bus(BUS_MANAGER, &bus); - if (r < 0) - return r; - - r = sd_bus_call_method( - bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "CreateSnapshot", - &error, - &reply, - "sb", n, false); - if (r < 0) - return log_error_errno(r, "Failed to create snapshot: %s", bus_error_message(&error, r)); - - r = sd_bus_message_read(reply, "o", &path); - if (r < 0) - return bus_log_parse_error(r); - - r = sd_bus_get_property_string( - bus, - "org.freedesktop.systemd1", - path, - "org.freedesktop.systemd1.Unit", - "Id", - &error, - &id); - if (r < 0) - return log_error_errno(r, "Failed to get ID of snapshot: %s", bus_error_message(&error, r)); - - if (!arg_quiet) - puts(id); - - return 0; -} - -static int delete_snapshot(int argc, char *argv[], void *userdata) { - _cleanup_strv_free_ char **names = NULL; - sd_bus *bus; - char **name; - int r; - - polkit_agent_open_if_enabled(); - - r = acquire_bus(BUS_MANAGER, &bus); - if (r < 0) - return r; - - r = expand_names(bus, strv_skip(argv, 1), ".snapshot", &names); - if (r < 0) - return log_error_errno(r, "Failed to expand names: %m"); - - STRV_FOREACH(name, names) { - _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; - int q; - - q = sd_bus_call_method( - bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "RemoveSnapshot", - &error, - NULL, - "s", *name); - if (q < 0) { - log_error_errno(q, "Failed to remove snapshot %s: %s", *name, bus_error_message(&error, q)); - if (r == 0) - r = q; - } - } - - return r; -} - static int daemon_reload(int argc, char *argv[], void *userdata) { _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; const char *method; @@ -5533,10 +5438,10 @@ static int enable_unit(int argc, char *argv[], void *userdata) { else assert_not_reached("Unknown verb"); - if (r < 0) { - log_error_errno(r, "Operation failed: %m"); - goto finish; - } + if (r == -ESHUTDOWN) + return log_error_errno(r, "Unit file is masked."); + if (r < 0) + return log_error_errno(r, "Operation failed: %m"); if (!arg_quiet) dump_unit_file_changes(changes, n_changes); @@ -5653,7 +5558,7 @@ static int enable_unit(int argc, char *argv[], void *userdata) { r = acquire_bus(BUS_MANAGER, &bus); if (r < 0) - return r; + goto finish; new_args[0] = (char*) (streq(argv[0], "enable") ? "start" : "stop"); for (i = 0; i < n_changes; i++) @@ -5699,7 +5604,8 @@ static int add_dependency(int argc, char *argv[], void *userdata) { unsigned n_changes = 0; r = unit_file_add_dependency(arg_scope, arg_runtime, arg_root, names, target, dep, arg_force, &changes, &n_changes); - + if (r == -ESHUTDOWN) + return log_error_errno(r, "Unit file is masked."); if (r < 0) return log_error_errno(r, "Can't add dependency: %m"); @@ -5836,8 +5742,8 @@ static int unit_is_enabled(int argc, char *argv[], void *userdata) { STRV_FOREACH(name, names) { UnitFileState state; - state = unit_file_get_state(arg_scope, arg_root, *name); - if (state < 0) + r = unit_file_get_state(arg_scope, arg_root, *name, &state); + if (r < 0) return log_error_errno(state, "Failed to get unit file state for %s: %m", *name); if (IN_SET(state, @@ -6381,9 +6287,6 @@ static void systemctl_help(void) { "Job Commands:\n" " list-jobs [PATTERN...] List jobs\n" " cancel [JOB...] Cancel all, one, or more jobs\n\n" - "Snapshot Commands:\n" - " snapshot [NAME] Create a snapshot\n" - " delete NAME... Remove one or more snapshots\n\n" "Environment Commands:\n" " show-environment Dump environment\n" " set-environment NAME=VALUE... Set one or more environment variables\n" @@ -6526,11 +6429,6 @@ static void help_states(void) { puts(slice_state_to_string(i)); if (!arg_no_legend) - puts("\nAvailable snapshot unit substates:"); - for (i = 0; i < _SNAPSHOT_STATE_MAX; i++) - puts(snapshot_state_to_string(i)); - - if (!arg_no_legend) puts("\nAvailable socket unit substates:"); for (i = 0; i < _SOCKET_STATE_MAX; i++) puts(socket_state_to_string(i)); @@ -6652,8 +6550,10 @@ static int systemctl_parse_argv(int argc, char *argv[]) { return version(); case 't': { - if (isempty(optarg)) - return log_error_errno(r, "--type requires arguments."); + if (isempty(optarg)) { + log_error("--type requires arguments."); + return -EINVAL; + } p = optarg; for(;;) { @@ -6885,8 +6785,10 @@ static int systemctl_parse_argv(int argc, char *argv[]) { break; case ARG_STATE: { - if (isempty(optarg)) - return log_error_errno(r, "--signal requires arguments."); + if (isempty(optarg)) { + log_error("--signal requires arguments."); + return -EINVAL; + } p = optarg; for(;;) { @@ -7439,7 +7341,7 @@ static int systemctl_main(int argc, char *argv[]) { { "list-jobs", VERB_ANY, VERB_ANY, 0, list_jobs }, { "list-machines", VERB_ANY, VERB_ANY, 0, list_machines }, { "clear-jobs", VERB_ANY, 1, 0, daemon_reload }, - { "cancel", 2, VERB_ANY, 0, cancel_job }, + { "cancel", VERB_ANY, VERB_ANY, 0, cancel_job }, { "start", 2, VERB_ANY, 0, start_unit }, { "stop", 2, VERB_ANY, 0, start_unit }, { "condstop", 2, VERB_ANY, 0, start_unit }, /* For compatibility with ALTLinux */ @@ -7460,8 +7362,6 @@ static int systemctl_main(int argc, char *argv[]) { { "cat", 2, VERB_ANY, 0, cat }, { "status", VERB_ANY, VERB_ANY, 0, show }, { "help", VERB_ANY, VERB_ANY, 0, show }, - { "snapshot", VERB_ANY, 2, 0, snapshot }, - { "delete", 2, VERB_ANY, 0, delete_snapshot }, { "daemon-reload", VERB_ANY, 1, 0, daemon_reload }, { "daemon-reexec", VERB_ANY, 1, 0, daemon_reload }, { "show-environment", VERB_ANY, 1, 0, show_environment }, @@ -7815,5 +7715,7 @@ finish: release_busses(); + /* Note that we return r here, not EXIT_SUCCESS, so that we can implement the LSB-like return codes */ + return r < 0 ? EXIT_FAILURE : r; } diff --git a/src/systemd/sd-bus.h b/src/systemd/sd-bus.h index 43cf247cdf..d8adf59aca 100644 --- a/src/systemd/sd-bus.h +++ b/src/systemd/sd-bus.h @@ -27,8 +27,9 @@ #include <sys/types.h> #include <sys/uio.h> -#include "sd-id128.h" #include "sd-event.h" +#include "sd-id128.h" + #include "_sd-common.h" _SD_BEGIN_DECLARATIONS; diff --git a/src/systemd/sd-daemon.h b/src/systemd/sd-daemon.h index 214e77cab1..c26cd1be3a 100644 --- a/src/systemd/sd-daemon.h +++ b/src/systemd/sd-daemon.h @@ -22,8 +22,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <sys/types.h> #include <inttypes.h> +#include <sys/types.h> #include "_sd-common.h" diff --git a/src/systemd/sd-device.h b/src/systemd/sd-device.h index fc11725821..edf80563ac 100644 --- a/src/systemd/sd-device.h +++ b/src/systemd/sd-device.h @@ -23,8 +23,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <sys/types.h> #include <inttypes.h> +#include <sys/types.h> #include "_sd-common.h" diff --git a/src/systemd/sd-dhcp-client.h b/src/systemd/sd-dhcp-client.h index c0146158f3..fc1d70e738 100644 --- a/src/systemd/sd-dhcp-client.h +++ b/src/systemd/sd-dhcp-client.h @@ -27,8 +27,8 @@ #include <netinet/in.h> #include <sys/types.h> -#include "sd-event.h" #include "sd-dhcp-lease.h" +#include "sd-event.h" #include "_sd-common.h" diff --git a/src/systemd/sd-dhcp-server.h b/src/systemd/sd-dhcp-server.h index 55bceb1ea5..56b63c38da 100644 --- a/src/systemd/sd-dhcp-server.h +++ b/src/systemd/sd-dhcp-server.h @@ -27,6 +27,7 @@ #include <netinet/in.h> #include "sd-event.h" + #include "_sd-common.h" _SD_BEGIN_DECLARATIONS; diff --git a/src/systemd/sd-dhcp6-client.h b/src/systemd/sd-dhcp6-client.h index 13182a481d..29e95e2492 100644 --- a/src/systemd/sd-dhcp6-client.h +++ b/src/systemd/sd-dhcp6-client.h @@ -26,8 +26,8 @@ #include <net/ethernet.h> #include <sys/types.h> -#include "sd-event.h" #include "sd-dhcp6-lease.h" +#include "sd-event.h" #include "_sd-common.h" @@ -49,6 +49,7 @@ int sd_dhcp6_client_set_callback(sd_dhcp6_client *client, sd_dhcp6_client_cb_t cb, void *userdata); int sd_dhcp6_client_set_index(sd_dhcp6_client *client, int interface_index); +int sd_dhcp6_client_set_local_address(sd_dhcp6_client *client, const struct in6_addr *local_address); int sd_dhcp6_client_set_mac(sd_dhcp6_client *client, const uint8_t *addr, size_t addr_len, uint16_t arp_type); int sd_dhcp6_client_set_duid(sd_dhcp6_client *client, uint16_t type, uint8_t *duid, @@ -62,6 +63,7 @@ int sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret); int sd_dhcp6_client_stop(sd_dhcp6_client *client); int sd_dhcp6_client_start(sd_dhcp6_client *client); +int sd_dhcp6_client_is_running(sd_dhcp6_client *client); int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event, int priority); int sd_dhcp6_client_detach_event(sd_dhcp6_client *client); diff --git a/src/systemd/sd-event.h b/src/systemd/sd-event.h index 565de5495a..fb97f7f28d 100644 --- a/src/systemd/sd-event.h +++ b/src/systemd/sd-event.h @@ -22,11 +22,11 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <sys/types.h> -#include <sys/signalfd.h> -#include <sys/epoll.h> #include <inttypes.h> #include <signal.h> +#include <sys/epoll.h> +#include <sys/signalfd.h> +#include <sys/types.h> #include "_sd-common.h" @@ -56,7 +56,8 @@ enum { SD_EVENT_PENDING, SD_EVENT_RUNNING, SD_EVENT_EXITING, - SD_EVENT_FINISHED + SD_EVENT_FINISHED, + SD_EVENT_PREPARING, }; enum { @@ -87,9 +88,9 @@ int sd_event_add_post(sd_event *e, sd_event_source **s, sd_event_handler_t callb int sd_event_add_exit(sd_event *e, sd_event_source **s, sd_event_handler_t callback, void *userdata); int sd_event_prepare(sd_event *e); -int sd_event_wait(sd_event *e, uint64_t timeout); +int sd_event_wait(sd_event *e, uint64_t usec); int sd_event_dispatch(sd_event *e); -int sd_event_run(sd_event *e, uint64_t timeout); +int sd_event_run(sd_event *e, uint64_t usec); int sd_event_loop(sd_event *e); int sd_event_exit(sd_event *e, int code); diff --git a/src/systemd/sd-ipv4acd.h b/src/systemd/sd-ipv4acd.h index 6337d61452..c1e79640eb 100644 --- a/src/systemd/sd-ipv4acd.h +++ b/src/systemd/sd-ipv4acd.h @@ -23,10 +23,11 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <netinet/in.h> #include <net/ethernet.h> +#include <netinet/in.h> #include "sd-event.h" + #include "_sd-common.h" _SD_BEGIN_DECLARATIONS; diff --git a/src/systemd/sd-ipv4ll.h b/src/systemd/sd-ipv4ll.h index 2949f1dfb2..1d25f02bd0 100644 --- a/src/systemd/sd-ipv4ll.h +++ b/src/systemd/sd-ipv4ll.h @@ -22,10 +22,11 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <netinet/in.h> #include <net/ethernet.h> +#include <netinet/in.h> #include "sd-event.h" + #include "_sd-common.h" _SD_BEGIN_DECLARATIONS; diff --git a/src/systemd/sd-journal.h b/src/systemd/sd-journal.h index 00237a2158..facb6d8a95 100644 --- a/src/systemd/sd-journal.h +++ b/src/systemd/sd-journal.h @@ -23,12 +23,13 @@ ***/ #include <inttypes.h> -#include <sys/types.h> #include <stdarg.h> +#include <sys/types.h> #include <sys/uio.h> #include <syslog.h> #include "sd-id128.h" + #include "_sd-common.h" /* Journal APIs. See sd-journal(3) for more information. */ diff --git a/src/systemd/sd-lldp.h b/src/systemd/sd-lldp.h index 31651ce132..16d297a52d 100644 --- a/src/systemd/sd-lldp.h +++ b/src/systemd/sd-lldp.h @@ -23,10 +23,11 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <net/ethernet.h> #include <inttypes.h> +#include <net/ethernet.h> #include "sd-event.h" + #include "_sd-common.h" _SD_BEGIN_DECLARATIONS; diff --git a/src/systemd/sd-login.h b/src/systemd/sd-login.h index 59c6eedcda..2ad6bcb357 100644 --- a/src/systemd/sd-login.h +++ b/src/systemd/sd-login.h @@ -22,8 +22,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <sys/types.h> #include <inttypes.h> +#include <sys/types.h> #include "_sd-common.h" diff --git a/src/systemd/sd-messages.h b/src/systemd/sd-messages.h index 8aedaec6d1..072832a916 100644 --- a/src/systemd/sd-messages.h +++ b/src/systemd/sd-messages.h @@ -23,6 +23,7 @@ ***/ #include "sd-id128.h" + #include "_sd-common.h" _SD_BEGIN_DECLARATIONS; diff --git a/src/systemd/sd-ndisc.h b/src/systemd/sd-ndisc.h index 570e1741d6..71e65d4425 100644 --- a/src/systemd/sd-ndisc.h +++ b/src/systemd/sd-ndisc.h @@ -26,24 +26,31 @@ #include <net/ethernet.h> #include "sd-event.h" + #include "_sd-common.h" _SD_BEGIN_DECLARATIONS; enum { - SD_NDISC_EVENT_ROUTER_ADVERTISMENT_NONE = 0, - SD_NDISC_EVENT_ROUTER_ADVERTISMENT_TIMEOUT = 1, - SD_NDISC_EVENT_ROUTER_ADVERTISMENT_OTHER = 2, - SD_NDISC_EVENT_ROUTER_ADVERTISMENT_MANAGED = 3, + SD_NDISC_EVENT_STOP = 0, + SD_NDISC_EVENT_TIMEOUT = 1, }; typedef struct sd_ndisc sd_ndisc; -typedef void(*sd_ndisc_callback_t)(sd_ndisc *nd, int event, - void *userdata); - -int sd_ndisc_set_callback(sd_ndisc *nd, sd_ndisc_callback_t cb, - void *userdata); +typedef void(*sd_ndisc_router_callback_t)(sd_ndisc *nd, uint8_t flags, const struct in6_addr *gateway, unsigned lifetime, int pref, void *userdata); +typedef void(*sd_ndisc_prefix_onlink_callback_t)(sd_ndisc *nd, const struct in6_addr *prefix, unsigned prefixlen, + unsigned lifetime, void *userdata); +typedef void(*sd_ndisc_prefix_autonomous_callback_t)(sd_ndisc *nd, const struct in6_addr *prefix, unsigned prefixlen, + unsigned lifetime_prefered, unsigned lifetime_valid, void *userdata); +typedef void(*sd_ndisc_callback_t)(sd_ndisc *nd, int event, void *userdata); + +int sd_ndisc_set_callback(sd_ndisc *nd, + sd_ndisc_router_callback_t rcb, + sd_ndisc_prefix_onlink_callback_t plcb, + sd_ndisc_prefix_autonomous_callback_t pacb, + sd_ndisc_callback_t cb, + void *userdata); int sd_ndisc_set_index(sd_ndisc *nd, int interface_index); int sd_ndisc_set_mac(sd_ndisc *nd, const struct ether_addr *mac_addr); diff --git a/src/systemd/sd-netlink.h b/src/systemd/sd-netlink.h index 8e1b06ee9a..dd5cc04ca6 100644 --- a/src/systemd/sd-netlink.h +++ b/src/systemd/sd-netlink.h @@ -23,12 +23,13 @@ ***/ #include <inttypes.h> -#include <netinet/in.h> #include <netinet/ether.h> +#include <netinet/in.h> #include <linux/rtnetlink.h> #include <linux/neighbour.h> #include "sd-event.h" + #include "_sd-common.h" _SD_BEGIN_DECLARATIONS; @@ -136,6 +137,8 @@ int sd_rtnl_message_link_get_type(sd_netlink_message *m, unsigned *type); int sd_rtnl_message_route_set_dst_prefixlen(sd_netlink_message *m, unsigned char prefixlen); int sd_rtnl_message_route_set_src_prefixlen(sd_netlink_message *m, unsigned char prefixlen); int sd_rtnl_message_route_set_scope(sd_netlink_message *m, unsigned char scope); +int sd_rtnl_message_route_set_flags(sd_netlink_message *m, unsigned flags); +int sd_rtnl_message_route_get_flags(sd_netlink_message *m, unsigned *flags); int sd_rtnl_message_route_get_family(sd_netlink_message *m, int *family); int sd_rtnl_message_route_get_protocol(sd_netlink_message *m, unsigned char *protocol); int sd_rtnl_message_route_get_scope(sd_netlink_message *m, unsigned char *scope); diff --git a/src/systemd/sd-network.h b/src/systemd/sd-network.h index 4179015fbf..076f45745d 100644 --- a/src/systemd/sd-network.h +++ b/src/systemd/sd-network.h @@ -23,8 +23,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <sys/types.h> #include <inttypes.h> +#include <sys/types.h> #include "_sd-common.h" diff --git a/src/systemd/sd-resolve.h b/src/systemd/sd-resolve.h index 82c4b39efe..bfe32102f8 100644 --- a/src/systemd/sd-resolve.h +++ b/src/systemd/sd-resolve.h @@ -28,6 +28,7 @@ #include <sys/types.h> #include "sd-event.h" + #include "_sd-common.h" _SD_BEGIN_DECLARATIONS; diff --git a/src/sysv-generator/sysv-generator.c b/src/sysv-generator/sysv-generator.c index 042be97840..5075548507 100644 --- a/src/sysv-generator/sysv-generator.c +++ b/src/sysv-generator/sysv-generator.c @@ -742,6 +742,7 @@ static int fix_order(SysvStub *s, Hashmap *all_services) { static int enumerate_sysv(const LookupPaths *lp, Hashmap *all_services) { char **path; + int r; assert(lp); assert(all_services); @@ -761,7 +762,6 @@ static int enumerate_sysv(const LookupPaths *lp, Hashmap *all_services) { _cleanup_free_ char *fpath = NULL, *name = NULL; _cleanup_(free_sysvstubp) SysvStub *service = NULL; struct stat st; - int r; if (fstatat(dirfd(d), de->d_name, &st, 0) < 0) { log_warning_errno(errno, "stat() failed on %s/%s, ignoring: %m", *path, de->d_name); @@ -781,8 +781,12 @@ static int enumerate_sysv(const LookupPaths *lp, Hashmap *all_services) { if (hashmap_contains(all_services, name)) continue; - if (unit_file_lookup_state(UNIT_FILE_SYSTEM, NULL, lp, name) >= 0) { - log_debug("Native unit for %s already exists, skipping", name); + r = unit_file_lookup_state(UNIT_FILE_SYSTEM, NULL, lp, name, NULL); + if (r < 0 && r != -ENOENT) { + log_debug_errno(r, "Failed to detect whether %s exists, skipping: %m", name); + continue; + } else if (r >= 0) { + log_debug("Native unit for %s already exists, skipping.", name); continue; } diff --git a/src/test/test-architecture.c b/src/test/test-architecture.c index a5b66a7d2f..35479d67c1 100644 --- a/src/test/test-architecture.c +++ b/src/test/test-architecture.c @@ -19,10 +19,10 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include "virt.h" #include "architecture.h" -#include "util.h" #include "log.h" +#include "util.h" +#include "virt.h" int main(int argc, char *argv[]) { int a, v; diff --git a/src/test/test-boot-timestamps.c b/src/test/test-boot-timestamps.c index 06d93af533..fab33d20c7 100644 --- a/src/test/test-boot-timestamps.c +++ b/src/test/test-boot-timestamps.c @@ -20,11 +20,11 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include "util.h" -#include "log.h" +#include "acpi-fpdt.h" #include "boot-timestamps.h" #include "efivars.h" -#include "acpi-fpdt.h" +#include "log.h" +#include "util.h" static int test_acpi_fpdt(void) { usec_t loader_start; diff --git a/src/test/test-calendarspec.c b/src/test/test-calendarspec.c index 70819b0371..9cef7154c6 100644 --- a/src/test/test-calendarspec.c +++ b/src/test/test-calendarspec.c @@ -75,7 +75,7 @@ static void test_next(const char *input, const char *new_tz, usec_t after, usec_ u = after; r = calendar_spec_next_usec(c, after, &u); - printf("At: %s\n", r < 0 ? strerror(-r) : format_timestamp(buf, sizeof(buf), u)); + printf("At: %s\n", r < 0 ? strerror(-r) : format_timestamp_us(buf, sizeof(buf), u)); if (expect != (usec_t)-1) assert_se(r >= 0 && u == expect); else @@ -123,6 +123,9 @@ int main(int argc, char* argv[]) { test_one("annually", "*-01-01 00:00:00"); test_one("*:2/3", "*-*-* *:02/3:00"); test_one("2015-10-25 01:00:00 uTc", "2015-10-25 01:00:00 UTC"); + test_one("2016-03-27 03:17:00.4200005", "2016-03-27 03:17:00.420001"); + test_one("2016-03-27 03:17:00/0.42", "2016-03-27 03:17:00/0.420000"); + test_one("2016-03-27 03:17:00/0.42", "2016-03-27 03:17:00/0.420000"); test_next("2016-03-27 03:17:00", "", 12345, 1459048620000000); test_next("2016-03-27 03:17:00", "CET", 12345, 1459041420000000); @@ -131,11 +134,19 @@ int main(int argc, char* argv[]) { test_next("2016-03-27 03:17:00 UTC", "", 12345, 1459048620000000); test_next("2016-03-27 03:17:00 UTC", "CET", 12345, 1459048620000000); test_next("2016-03-27 03:17:00 UTC", "EET", 12345, 1459048620000000); + test_next("2016-03-27 03:17:00.420000001 UTC", "EET", 12345, 1459048620420000); + test_next("2016-03-27 03:17:00.4200005 UTC", "EET", 12345, 1459048620420001); + test_next("2015-11-13 09:11:23.42", "EET", 12345, 1447398683420000); + test_next("2015-11-13 09:11:23.42/1.77", "EET", 1447398683420000, 1447398685190000); + test_next("2015-11-13 09:11:23.42/1.77", "EET", 1447398683419999, 1447398683420000); assert_se(calendar_spec_from_string("test", &c) < 0); assert_se(calendar_spec_from_string("", &c) < 0); assert_se(calendar_spec_from_string("7", &c) < 0); assert_se(calendar_spec_from_string("121212:1:2", &c) < 0); + assert_se(calendar_spec_from_string("2000-03-05.23 00:00:00", &c) < 0); + assert_se(calendar_spec_from_string("2000-03-05 00:00.1:00", &c) < 0); + assert_se(calendar_spec_from_string("00:00:00/0.00000001", &c) < 0); return 0; } diff --git a/src/test/test-cgroup-mask.c b/src/test/test-cgroup-mask.c index de6c421b82..2746013522 100644 --- a/src/test/test-cgroup-mask.c +++ b/src/test/test-cgroup-mask.c @@ -21,10 +21,10 @@ #include <stdio.h> -#include "manager.h" -#include "unit.h" #include "macro.h" +#include "manager.h" #include "test-helper.h" +#include "unit.h" static int test_cgroup_mask(void) { Manager *m = NULL; @@ -40,6 +40,16 @@ static int test_cgroup_mask(void) { puts("manager_new: Permission denied. Skipping test."); return EXIT_TEST_SKIP; } + + /* Turn off all kinds of default accouning, so that we can + * verify the masks resulting of our configuration and nothing + * else. */ + m->default_cpu_accounting = + m->default_memory_accounting = + m->default_blockio_accounting = + m->default_tasks_accounting = false; + m->default_tasks_max = (uint64_t) -1; + assert_se(r >= 0); assert_se(manager_startup(m, serial, fdset) >= 0); diff --git a/src/test/test-conf-files.c b/src/test/test-conf-files.c index a69698d4ea..86ac513d4f 100644 --- a/src/test/test-conf-files.c +++ b/src/test/test-conf-files.c @@ -26,6 +26,7 @@ #include "conf-files.h" #include "fs-util.h" #include "macro.h" +#include "parse-util.h" #include "rm-rf.h" #include "string-util.h" #include "strv.h" @@ -40,7 +41,7 @@ static void setup_test_dir(char *tmp_dir, const char *files, ...) { va_start(ap, files); while (files != NULL) { _cleanup_free_ char *path = strappend(tmp_dir, files); - assert_se(touch_file(path, true, USEC_INFINITY, UID_INVALID, GID_INVALID, 0) == 0); + assert_se(touch_file(path, true, USEC_INFINITY, UID_INVALID, GID_INVALID, MODE_INVALID) == 0); files = va_arg(ap, const char *); } va_end(ap); diff --git a/src/test/test-dns-domain.c b/src/test/test-dns-domain.c index d5778748a0..f010e4e19a 100644 --- a/src/test/test-dns-domain.c +++ b/src/test/test-dns-domain.c @@ -52,6 +52,36 @@ static void test_dns_label_unescape(void) { test_dns_label_unescape_one("foobar.", "foobar", 20, 6); } +static void test_dns_name_to_wire_format_one(const char *what, const char *expect, size_t buffer_sz, int ret) { + uint8_t buffer[buffer_sz]; + int r; + + r = dns_name_to_wire_format(what, buffer, buffer_sz); + assert_se(r == ret); + + if (r < 0) + return; + + assert_se(!memcmp(buffer, expect, r)); +} + +static void test_dns_name_to_wire_format(void) { + const char out1[] = { 3, 'f', 'o', 'o', 0 }; + const char out2[] = { 5, 'h', 'a', 'l', 'l', 'o', 3, 'f', 'o', 'o', 3, 'b', 'a', 'r', 0 }; + const char out3[] = { 4, ' ', 'f', 'o', 'o', 3, 'b', 'a', 'r', 0 }; + + test_dns_name_to_wire_format_one("", NULL, 0, -EINVAL); + + test_dns_name_to_wire_format_one("foo", out1, sizeof(out1), sizeof(out1)); + test_dns_name_to_wire_format_one("foo", out1, sizeof(out1) + 1, sizeof(out1)); + test_dns_name_to_wire_format_one("foo", out1, sizeof(out1) - 1, -ENOBUFS); + + test_dns_name_to_wire_format_one("hallo.foo.bar", out2, sizeof(out2), sizeof(out2)); + test_dns_name_to_wire_format_one("hallo.foo..bar", NULL, 32, -EINVAL); + + test_dns_name_to_wire_format_one("\\032foo.bar", out3, sizeof(out3), sizeof(out3)); +} + static void test_dns_label_unescape_suffix_one(const char *what, const char *expect1, const char *expect2, size_t buffer_sz, int ret1, int ret2) { char buffer[buffer_sz]; const char *label; @@ -96,7 +126,7 @@ static void test_dns_label_escape_one(const char *what, size_t l, const char *ex _cleanup_free_ char *t = NULL; int r; - r = dns_label_escape(what, l, &t); + r = dns_label_escape_new(what, l, &t); assert_se(r == ret); if (r < 0) @@ -216,21 +246,21 @@ static void test_dns_name_endswith(void) { test_dns_name_endswith_one("x.y\001.z", "waldo", -EINVAL); } -static void test_dns_name_root(void) { - assert_se(dns_name_root("") == true); - assert_se(dns_name_root(".") == true); - assert_se(dns_name_root("xxx") == false); - assert_se(dns_name_root("xxx.") == false); - assert_se(dns_name_root("..") == -EINVAL); +static void test_dns_name_is_root(void) { + assert_se(dns_name_is_root("")); + assert_se(dns_name_is_root(".")); + assert_se(!dns_name_is_root("xxx")); + assert_se(!dns_name_is_root("xxx.")); + assert_se(!dns_name_is_root("..")); } -static void test_dns_name_single_label(void) { - assert_se(dns_name_single_label("") == false); - assert_se(dns_name_single_label(".") == false); - assert_se(dns_name_single_label("..") == -EINVAL); - assert_se(dns_name_single_label("x") == true); - assert_se(dns_name_single_label("x.") == true); - assert_se(dns_name_single_label("xx.yy") == false); +static void test_dns_name_is_single_label(void) { + assert_se(!dns_name_is_single_label("")); + assert_se(!dns_name_is_single_label(".")); + assert_se(!dns_name_is_single_label("..")); + assert_se(dns_name_is_single_label("x")); + assert_se(dns_name_is_single_label("x.")); + assert_se(!dns_name_is_single_label("xx.yy")); } static void test_dns_name_reverse_one(const char *address, const char *name) { @@ -286,6 +316,117 @@ static void test_dns_name_is_valid(void) { test_dns_name_is_valid_one("\n", 0); } +static void test_dns_service_name_is_valid(void) { + assert_se(dns_service_name_is_valid("Lennart's Compüter")); + assert_se(dns_service_name_is_valid("piff.paff")); + + assert_se(!dns_service_name_is_valid(NULL)); + assert_se(!dns_service_name_is_valid("")); + assert_se(!dns_service_name_is_valid("foo\nbar")); + assert_se(!dns_service_name_is_valid("foo\201bar")); + assert_se(!dns_service_name_is_valid("this is an overly long string that is certainly longer than 63 characters")); +} + +static void test_dns_srv_type_is_valid(void) { + + assert_se(dns_srv_type_is_valid("_http._tcp")); + assert_se(dns_srv_type_is_valid("_foo-bar._tcp")); + assert_se(dns_srv_type_is_valid("_w._udp")); + assert_se(dns_srv_type_is_valid("_a800._tcp")); + assert_se(dns_srv_type_is_valid("_a-800._tcp")); + + assert_se(!dns_srv_type_is_valid(NULL)); + assert_se(!dns_srv_type_is_valid("")); + assert_se(!dns_srv_type_is_valid("x")); + assert_se(!dns_srv_type_is_valid("_foo")); + assert_se(!dns_srv_type_is_valid("_tcp")); + assert_se(!dns_srv_type_is_valid("_")); + assert_se(!dns_srv_type_is_valid("_foo.")); + assert_se(!dns_srv_type_is_valid("_föo._tcp")); + assert_se(!dns_srv_type_is_valid("_f\no._tcp")); + assert_se(!dns_srv_type_is_valid("_800._tcp")); + assert_se(!dns_srv_type_is_valid("_-800._tcp")); + assert_se(!dns_srv_type_is_valid("_-foo._tcp")); + assert_se(!dns_srv_type_is_valid("_piep._foo._udp")); +} + +static void test_dns_service_join_one(const char *a, const char *b, const char *c, int r, const char *d) { + _cleanup_free_ char *x = NULL, *y = NULL, *z = NULL, *t = NULL; + + assert_se(dns_service_join(a, b, c, &t) == r); + assert_se(streq_ptr(t, d)); + + if (r < 0) + return; + + assert_se(dns_service_split(t, &x, &y, &z) >= 0); + assert_se(streq_ptr(a, x)); + assert_se(streq_ptr(b, y)); + assert_se(streq_ptr(c, z)); +} + +static void test_dns_service_join(void) { + test_dns_service_join_one("", "", "", -EINVAL, NULL); + test_dns_service_join_one("", "_http._tcp", "", -EINVAL, NULL); + test_dns_service_join_one("", "_http._tcp", "foo", -EINVAL, NULL); + test_dns_service_join_one("foo", "", "foo", -EINVAL, NULL); + test_dns_service_join_one("foo", "foo", "foo", -EINVAL, NULL); + + test_dns_service_join_one("foo", "_http._tcp", "", 0, "foo._http._tcp"); + test_dns_service_join_one(NULL, "_http._tcp", "", 0, "_http._tcp"); + test_dns_service_join_one("foo", "_http._tcp", "foo", 0, "foo._http._tcp.foo"); + test_dns_service_join_one(NULL, "_http._tcp", "foo", 0, "_http._tcp.foo"); + test_dns_service_join_one("Lennart's PC", "_pc._tcp", "foo.bar.com", 0, "Lennart\\039s\\032PC._pc._tcp.foo.bar.com"); + test_dns_service_join_one(NULL, "_pc._tcp", "foo.bar.com", 0, "_pc._tcp.foo.bar.com"); +} + +static void test_dns_service_split_one(const char *joined, const char *a, const char *b, const char *c, int r) { + _cleanup_free_ char *x = NULL, *y = NULL, *z = NULL, *t = NULL; + + assert_se(dns_service_split(joined, &x, &y, &z) == r); + assert_se(streq_ptr(x, a)); + assert_se(streq_ptr(y, b)); + assert_se(streq_ptr(z, c)); + + if (r < 0) + return; + + if (y) { + assert_se(dns_service_join(x, y, z, &t) == 0); + assert_se(streq_ptr(joined, t)); + } else + assert_se(!x && streq_ptr(z, joined)); +} + +static void test_dns_service_split(void) { + test_dns_service_split_one("", NULL, NULL, "", 0); + test_dns_service_split_one("foo", NULL, NULL, "foo", 0); + test_dns_service_split_one("foo.bar", NULL, NULL, "foo.bar", 0); + test_dns_service_split_one("_foo.bar", NULL, NULL, "_foo.bar", 0); + test_dns_service_split_one("_foo._bar", NULL, "_foo._bar", "", 0); + test_dns_service_split_one("_meh._foo._bar", "_meh", "_foo._bar", "", 0); + test_dns_service_split_one("Wuff\\032Wuff._foo._bar.waldo.com", "Wuff Wuff", "_foo._bar", "waldo.com", 0); +} + +static void test_dns_name_change_suffix_one(const char *name, const char *old_suffix, const char *new_suffix, int r, const char *result) { + _cleanup_free_ char *s = NULL; + + assert_se(dns_name_change_suffix(name, old_suffix, new_suffix, &s) == r); + assert_se(streq_ptr(s, result)); +} + +static void test_dns_name_change_suffix(void) { + test_dns_name_change_suffix_one("foo.bar", "bar", "waldo", 1, "foo.waldo"); + test_dns_name_change_suffix_one("foo.bar.waldi.quux", "foo.bar.waldi.quux", "piff.paff", 1, "piff.paff"); + test_dns_name_change_suffix_one("foo.bar.waldi.quux", "bar.waldi.quux", "piff.paff", 1, "foo.piff.paff"); + test_dns_name_change_suffix_one("foo.bar.waldi.quux", "waldi.quux", "piff.paff", 1, "foo.bar.piff.paff"); + test_dns_name_change_suffix_one("foo.bar.waldi.quux", "quux", "piff.paff", 1, "foo.bar.waldi.piff.paff"); + test_dns_name_change_suffix_one("foo.bar.waldi.quux", "", "piff.paff", 1, "foo.bar.waldi.quux.piff.paff"); + test_dns_name_change_suffix_one("", "", "piff.paff", 1, "piff.paff"); + test_dns_name_change_suffix_one("", "", "", 1, ""); + test_dns_name_change_suffix_one("a", "b", "c", 0, NULL); +} + int main(int argc, char *argv[]) { test_dns_label_unescape(); @@ -295,11 +436,17 @@ int main(int argc, char *argv[]) { test_dns_name_equal(); test_dns_name_endswith(); test_dns_name_between(); - test_dns_name_root(); - test_dns_name_single_label(); + test_dns_name_is_root(); + test_dns_name_is_single_label(); test_dns_name_reverse(); test_dns_name_concat(); test_dns_name_is_valid(); + test_dns_name_to_wire_format(); + test_dns_service_name_is_valid(); + test_dns_srv_type_is_valid(); + test_dns_service_join(); + test_dns_service_split(); + test_dns_name_change_suffix(); return 0; } diff --git a/src/test/test-engine.c b/src/test/test-engine.c index 6596069ade..4f14c58788 100644 --- a/src/test/test-engine.c +++ b/src/test/test-engine.c @@ -19,12 +19,12 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <stdio.h> #include <errno.h> +#include <stdio.h> #include <string.h> -#include "manager.h" #include "bus-util.h" +#include "manager.h" int main(int argc, char *argv[]) { _cleanup_bus_error_free_ sd_bus_error err = SD_BUS_ERROR_NULL; @@ -52,7 +52,7 @@ int main(int argc, char *argv[]) { manager_dump_units(m, stdout, "\t"); printf("Test1: (Trivial)\n"); - r = manager_add_job(m, JOB_START, c, JOB_REPLACE, false, &err, &j); + r = manager_add_job(m, JOB_START, c, JOB_REPLACE, &err, &j); if (sd_bus_error_is_set(&err)) log_error("error: %s: %s", err.name, err.message); assert_se(r == 0); @@ -65,15 +65,15 @@ int main(int argc, char *argv[]) { manager_dump_units(m, stdout, "\t"); printf("Test2: (Cyclic Order, Unfixable)\n"); - assert_se(manager_add_job(m, JOB_START, d, JOB_REPLACE, false, NULL, &j) == -EDEADLK); + assert_se(manager_add_job(m, JOB_START, d, JOB_REPLACE, NULL, &j) == -EDEADLK); manager_dump_jobs(m, stdout, "\t"); printf("Test3: (Cyclic Order, Fixable, Garbage Collector)\n"); - assert_se(manager_add_job(m, JOB_START, e, JOB_REPLACE, false, NULL, &j) == 0); + assert_se(manager_add_job(m, JOB_START, e, JOB_REPLACE, NULL, &j) == 0); manager_dump_jobs(m, stdout, "\t"); printf("Test4: (Identical transaction)\n"); - assert_se(manager_add_job(m, JOB_START, e, JOB_FAIL, false, NULL, &j) == 0); + assert_se(manager_add_job(m, JOB_START, e, JOB_FAIL, NULL, &j) == 0); manager_dump_jobs(m, stdout, "\t"); printf("Load3:\n"); @@ -81,21 +81,21 @@ int main(int argc, char *argv[]) { manager_dump_units(m, stdout, "\t"); printf("Test5: (Colliding transaction, fail)\n"); - assert_se(manager_add_job(m, JOB_START, g, JOB_FAIL, false, NULL, &j) == -EDEADLK); + assert_se(manager_add_job(m, JOB_START, g, JOB_FAIL, NULL, &j) == -EDEADLK); printf("Test6: (Colliding transaction, replace)\n"); - assert_se(manager_add_job(m, JOB_START, g, JOB_REPLACE, false, NULL, &j) == 0); + assert_se(manager_add_job(m, JOB_START, g, JOB_REPLACE, NULL, &j) == 0); manager_dump_jobs(m, stdout, "\t"); printf("Test7: (Unmergeable job type, fail)\n"); - assert_se(manager_add_job(m, JOB_STOP, g, JOB_FAIL, false, NULL, &j) == -EDEADLK); + assert_se(manager_add_job(m, JOB_STOP, g, JOB_FAIL, NULL, &j) == -EDEADLK); printf("Test8: (Mergeable job type, fail)\n"); - assert_se(manager_add_job(m, JOB_RESTART, g, JOB_FAIL, false, NULL, &j) == 0); + assert_se(manager_add_job(m, JOB_RESTART, g, JOB_FAIL, NULL, &j) == 0); manager_dump_jobs(m, stdout, "\t"); printf("Test9: (Unmergeable job type, replace)\n"); - assert_se(manager_add_job(m, JOB_STOP, g, JOB_REPLACE, false, NULL, &j) == 0); + assert_se(manager_add_job(m, JOB_STOP, g, JOB_REPLACE, NULL, &j) == 0); manager_dump_jobs(m, stdout, "\t"); printf("Load4:\n"); @@ -103,7 +103,7 @@ int main(int argc, char *argv[]) { manager_dump_units(m, stdout, "\t"); printf("Test10: (Unmergeable job type of auxiliary job, fail)\n"); - assert_se(manager_add_job(m, JOB_START, h, JOB_FAIL, false, NULL, &j) == 0); + assert_se(manager_add_job(m, JOB_START, h, JOB_FAIL, NULL, &j) == 0); manager_dump_jobs(m, stdout, "\t"); manager_free(m); diff --git a/src/test/test-execute.c b/src/test/test-execute.c index e2ec53ee51..03ec0fcfc7 100644 --- a/src/test/test-execute.c +++ b/src/test/test-execute.c @@ -168,6 +168,30 @@ static void test_exec_environmentfile(Manager *m) { unlink("/tmp/test-exec_environmentfile.conf"); } +static void test_exec_passenvironment(Manager *m) { + /* test-execute runs under MANAGER_USER which, by default, forwards all + * variables present in the environment, but only those that are + * present _at the time it is created_! + * + * So these PassEnvironment checks are still expected to work, since we + * are ensuring the variables are not present at manager creation (they + * are unset explicitly in main) and are only set here. + * + * This is still a good approximation of how a test for MANAGER_SYSTEM + * would work. + */ + assert_se(setenv("VAR1", "word1 word2", 1) == 0); + assert_se(setenv("VAR2", "word3", 1) == 0); + assert_se(setenv("VAR3", "$word 5 6", 1) == 0); + test(m, "exec-passenvironment.service", 0, CLD_EXITED); + test(m, "exec-passenvironment-repeated.service", 0, CLD_EXITED); + test(m, "exec-passenvironment-empty.service", 0, CLD_EXITED); + assert_se(unsetenv("VAR1") == 0); + assert_se(unsetenv("VAR2") == 0); + assert_se(unsetenv("VAR3") == 0); + test(m, "exec-passenvironment-absent.service", 0, CLD_EXITED); +} + static void test_exec_umask(Manager *m) { test(m, "exec-umask-default.service", 0, CLD_EXITED); test(m, "exec-umask-0177.service", 0, CLD_EXITED); @@ -237,6 +261,7 @@ int main(int argc, char *argv[]) { test_exec_group, test_exec_environment, test_exec_environmentfile, + test_exec_passenvironment, test_exec_umask, test_exec_runtimedirectory, test_exec_capabilityboundingset, @@ -260,6 +285,16 @@ int main(int argc, char *argv[]) { assert_se(setenv("XDG_RUNTIME_DIR", "/tmp/", 1) == 0); assert_se(set_unit_path(TEST_DIR "/test-execute/") >= 0); + /* Unset VAR1, VAR2 and VAR3 which are used in the PassEnvironment test + * cases, otherwise (and if they are present in the environment), + * `manager_default_environment` will copy them into the default + * environment which is passed to each created job, which will make the + * tests that expect those not to be present to fail. + */ + assert_se(unsetenv("VAR1") == 0); + assert_se(unsetenv("VAR2") == 0); + assert_se(unsetenv("VAR3") == 0); + r = manager_new(MANAGER_USER, true, &m); if (IN_SET(r, -EPERM, -EACCES, -EADDRINUSE, -EHOSTDOWN, -ENOENT)) { printf("Skipping test: manager_new: %s", strerror(-r)); diff --git a/src/test/test-extract-word.c b/src/test/test-extract-word.c index 09698c07c7..65d3a0a96e 100644 --- a/src/test/test-extract-word.c +++ b/src/test/test-extract-word.c @@ -325,6 +325,18 @@ static void test_extract_first_word(void) { assert_se(extract_first_word(&p, &t, ":", EXTRACT_DONT_COALESCE_SEPARATORS) == 0); assert_se(!t); assert_se(!p); + + p = "foo\\xbar"; + assert_se(extract_first_word(&p, &t, NULL, 0) > 0); + assert_se(streq(t, "fooxbar")); + free(t); + assert_se(p == NULL); + + p = "foo\\xbar"; + assert_se(extract_first_word(&p, &t, NULL, EXTRACT_RETAIN_ESCAPE) > 0); + assert_se(streq(t, "foo\\xbar")); + free(t); + assert_se(p == NULL); } static void test_extract_first_word_and_warn(void) { diff --git a/src/test/test-fileio.c b/src/test/test-fileio.c index e588681b86..871c71e171 100644 --- a/src/test/test-fileio.c +++ b/src/test/test-fileio.c @@ -19,8 +19,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <stdio.h> #include <fcntl.h> +#include <stdio.h> #include <unistd.h> #include "alloc-util.h" @@ -363,6 +363,26 @@ static void test_write_string_file_no_create(void) { unlink(fn); } +static void test_write_string_file_verify(void) { + _cleanup_free_ char *buf = NULL, *buf2 = NULL; + int r; + + assert_se(read_one_line_file("/proc/cmdline", &buf) >= 0); + assert_se((buf2 = strjoin(buf, "\n", NULL))); + + r = write_string_file("/proc/cmdline", buf, 0); + assert_se(r == -EACCES || r == -EIO); + r = write_string_file("/proc/cmdline", buf2, 0); + assert_se(r == -EACCES || r == -EIO); + + assert_se(write_string_file("/proc/cmdline", buf, WRITE_STRING_FILE_VERIFY_ON_FAILURE) == 0); + assert_se(write_string_file("/proc/cmdline", buf2, WRITE_STRING_FILE_VERIFY_ON_FAILURE) == 0); + + r = write_string_file("/proc/cmdline", buf, WRITE_STRING_FILE_VERIFY_ON_FAILURE|WRITE_STRING_FILE_AVOID_NEWLINE); + assert_se(r == -EACCES || r == -EIO); + assert_se(write_string_file("/proc/cmdline", buf2, WRITE_STRING_FILE_VERIFY_ON_FAILURE|WRITE_STRING_FILE_AVOID_NEWLINE) == 0); +} + static void test_load_env_file_pairs(void) { char fn[] = "/tmp/test-load_env_file_pairs-XXXXXX"; int fd; @@ -419,6 +439,7 @@ int main(int argc, char *argv[]) { test_write_string_stream(); test_write_string_file(); test_write_string_file_no_create(); + test_write_string_file_verify(); test_load_env_file_pairs(); return 0; diff --git a/src/test/test-firewall-util.c b/src/test/test-firewall-util.c index d636e427c4..ff66bde094 100644 --- a/src/test/test-firewall-util.c +++ b/src/test/test-firewall-util.c @@ -19,8 +19,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include "log.h" #include "firewall-util.h" +#include "log.h" #define MAKE_IN_ADDR_UNION(a,b,c,d) (union in_addr_union) { .in.s_addr = htobe32((uint32_t) (a) << 24 | (uint32_t) (b) << 16 | (uint32_t) (c) << 8 | (uint32_t) (d))} diff --git a/src/test/test-hashmap.c b/src/test/test-hashmap.c index d0e65001f5..83cea360e6 100644 --- a/src/test/test-hashmap.c +++ b/src/test/test-hashmap.c @@ -17,8 +17,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include "util.h" #include "hashmap.h" +#include "util.h" void test_hashmap_funcs(void); void test_ordered_hashmap_funcs(void); diff --git a/src/test/test-install-root.c b/src/test/test-install-root.c new file mode 100644 index 0000000000..08fde94f7f --- /dev/null +++ b/src/test/test-install-root.c @@ -0,0 +1,665 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2011 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 "alloc-util.h" +#include "fileio.h" +#include "install.h" +#include "mkdir.h" +#include "rm-rf.h" +#include "string-util.h" + +static void test_basic_mask_and_enable(const char *root) { + const char *p; + UnitFileState state; + UnitFileChange *changes = NULL; + unsigned n_changes = 0; + + 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); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", NULL) == -ENOENT); + + p = strjoina(root, "/usr/lib/systemd/system/a.service"); + assert_se(write_string_file(p, + "[Install]\n" + "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0); + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "a.service", NULL) >= 0); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "a.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + + p = strjoina(root, "/usr/lib/systemd/system/b.service"); + assert_se(symlink("a.service", p) >= 0); + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", NULL) >= 0); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + + p = strjoina(root, "/usr/lib/systemd/system/c.service"); + assert_se(symlink("/usr/lib/systemd/system/a.service", p) >= 0); + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", NULL) >= 0); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + + p = strjoina(root, "/usr/lib/systemd/system/d.service"); + assert_se(symlink("c.service", p) >= 0); + + /* This one is interesting, as d follows a relative, then an absolute symlink */ + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", NULL) >= 0); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + + assert_se(unit_file_mask(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("a.service"), false, &changes, &n_changes) >= 0); + assert_se(n_changes == 1); + assert_se(changes[0].type == UNIT_FILE_SYMLINK); + assert_se(streq(changes[0].source, "/dev/null")); + p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/a.service"); + assert_se(streq(changes[0].path, p)); + + unit_file_changes_free(changes, n_changes); + changes = NULL; n_changes = 0; + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "a.service", &state) >= 0 && state == UNIT_FILE_MASKED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", &state) >= 0 && state == UNIT_FILE_MASKED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_MASKED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_MASKED); + + /* Enabling a masked unit should fail! */ + assert_se(unit_file_enable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("a.service"), false, &changes, &n_changes) == -ESHUTDOWN); + unit_file_changes_free(changes, n_changes); + changes = NULL; n_changes = 0; + + assert_se(unit_file_unmask(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("a.service"), &changes, &n_changes) >= 0); + assert_se(n_changes == 1); + assert_se(changes[0].type == UNIT_FILE_UNLINK); + p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/a.service"); + assert_se(streq(changes[0].path, p)); + unit_file_changes_free(changes, n_changes); + changes = NULL; n_changes = 0; + + assert_se(unit_file_enable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("a.service"), false, &changes, &n_changes) == 1); + assert_se(n_changes == 1); + assert_se(changes[0].type == UNIT_FILE_SYMLINK); + assert_se(streq(changes[0].source, "/usr/lib/systemd/system/a.service")); + p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/a.service"); + assert_se(streq(changes[0].path, p)); + unit_file_changes_free(changes, n_changes); + changes = NULL; n_changes = 0; + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "a.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + + /* Enabling it again should succeed but be a NOP */ + assert_se(unit_file_enable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("a.service"), false, &changes, &n_changes) == 1); + assert_se(n_changes == 0); + unit_file_changes_free(changes, n_changes); + changes = NULL; n_changes = 0; + + assert_se(unit_file_disable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("a.service"), &changes, &n_changes) >= 0); + assert_se(n_changes == 1); + assert_se(changes[0].type == UNIT_FILE_UNLINK); + p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/a.service"); + assert_se(streq(changes[0].path, p)); + unit_file_changes_free(changes, n_changes); + changes = NULL; n_changes = 0; + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "a.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + + /* Disabling a disabled unit must suceed but be a NOP */ + assert_se(unit_file_disable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("a.service"), &changes, &n_changes) >= 0); + assert_se(n_changes == 0); + unit_file_changes_free(changes, n_changes); + changes = NULL; n_changes = 0; + + /* Let's enable this indirectly via a symlink */ + assert_se(unit_file_enable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("d.service"), false, &changes, &n_changes) >= 0); + assert_se(n_changes == 1); + assert_se(changes[0].type == UNIT_FILE_SYMLINK); + assert_se(streq(changes[0].source, "/usr/lib/systemd/system/a.service")); + p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/a.service"); + assert_se(streq(changes[0].path, p)); + unit_file_changes_free(changes, n_changes); + changes = NULL; n_changes = 0; + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "a.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + + /* Let's try to reenable */ + + assert_se(unit_file_reenable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("b.service"), false, &changes, &n_changes) >= 0); + assert_se(n_changes == 2); + assert_se(changes[0].type == UNIT_FILE_UNLINK); + p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/a.service"); + assert_se(streq(changes[0].path, p)); + assert_se(changes[1].type == UNIT_FILE_SYMLINK); + assert_se(streq(changes[1].source, "/usr/lib/systemd/system/a.service")); + assert_se(streq(changes[1].path, p)); + unit_file_changes_free(changes, n_changes); + changes = NULL; n_changes = 0; + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "a.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_ENABLED); +} + +static void test_linked_units(const char *root) { + const char *p, *q; + UnitFileState state; + UnitFileChange *changes = NULL; + unsigned n_changes = 0, i; + + /* + * We'll test three cases here: + * + * a) a unit file in /opt, that we use "systemctl link" and + * "systemctl enable" on to make it available to the system + * + * b) a unit file in /opt, that is statically linked into + * /usr/lib/systemd/system, that "enable" should work on + * correctly. + * + * c) a unit file in /opt, that is linked into + * /etc/systemd/system, and where "enable" should result in + * -ELOOP, since using information from /etc to generate + * information in /etc should not be allowed. + */ + + p = strjoina(root, "/opt/linked.service"); + assert_se(write_string_file(p, + "[Install]\n" + "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0); + + p = strjoina(root, "/opt/linked2.service"); + assert_se(write_string_file(p, + "[Install]\n" + "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0); + + p = strjoina(root, "/opt/linked3.service"); + assert_se(write_string_file(p, + "[Install]\n" + "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0); + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "linked.service", NULL) == -ENOENT); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "linked2.service", NULL) == -ENOENT); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "linked3.service", NULL) == -ENOENT); + + p = strjoina(root, "/usr/lib/systemd/system/linked2.service"); + assert_se(symlink("/opt/linked2.service", p) >= 0); + + p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/linked3.service"); + assert_se(symlink("/opt/linked3.service", p) >= 0); + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "linked.service", &state) == -ENOENT); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "linked2.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "linked3.service", &state) >= 0 && state == UNIT_FILE_LINKED); + + /* First, let's link the unit into the search path */ + assert_se(unit_file_link(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("/opt/linked.service"), false, &changes, &n_changes) >= 0); + assert_se(n_changes == 1); + assert_se(changes[0].type == UNIT_FILE_SYMLINK); + assert_se(streq(changes[0].source, "/opt/linked.service")); + p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/linked.service"); + assert_se(streq(changes[0].path, p)); + unit_file_changes_free(changes, n_changes); + changes = NULL; n_changes = 0; + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "linked.service", &state) >= 0 && state == UNIT_FILE_LINKED); + + /* Let's unlink it from the search path again */ + assert_se(unit_file_disable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("linked.service"), &changes, &n_changes) >= 0); + assert_se(n_changes == 1); + assert_se(changes[0].type == UNIT_FILE_UNLINK); + p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/linked.service"); + assert_se(streq(changes[0].path, p)); + unit_file_changes_free(changes, n_changes); + changes = NULL; n_changes = 0; + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "linked.service", NULL) == -ENOENT); + + /* Now, let's not just link it, but also enable it */ + assert_se(unit_file_enable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("/opt/linked.service"), false, &changes, &n_changes) >= 0); + assert_se(n_changes == 2); + p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/linked.service"); + q = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/linked.service"); + for (i = 0 ; i < n_changes; i++) { + assert_se(changes[i].type == UNIT_FILE_SYMLINK); + assert_se(streq(changes[i].source, "/opt/linked.service")); + + if (p && streq(changes[i].path, p)) + p = NULL; + else if (q && streq(changes[i].path, q)) + q = NULL; + else + assert_not_reached("wut?"); + } + assert(!p && !q); + unit_file_changes_free(changes, n_changes); + changes = NULL; n_changes = 0; + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "linked.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + + /* And let's unlink it again */ + assert_se(unit_file_disable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("linked.service"), &changes, &n_changes) >= 0); + assert_se(n_changes == 2); + p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/linked.service"); + q = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/linked.service"); + for (i = 0; i < n_changes; i++) { + assert_se(changes[i].type == UNIT_FILE_UNLINK); + + if (p && streq(changes[i].path, p)) + p = NULL; + else if (q && streq(changes[i].path, q)) + q = NULL; + else + assert_not_reached("wut?"); + } + assert(!p && !q); + unit_file_changes_free(changes, n_changes); + changes = NULL; n_changes = 0; + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "linked.service", NULL) == -ENOENT); + + assert_se(unit_file_enable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("linked2.service"), false, &changes, &n_changes) >= 0); + assert_se(n_changes == 2); + p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/linked2.service"); + q = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/linked2.service"); + for (i = 0 ; i < n_changes; i++) { + assert_se(changes[i].type == UNIT_FILE_SYMLINK); + assert_se(streq(changes[i].source, "/opt/linked2.service")); + + if (p && streq(changes[i].path, p)) + p = NULL; + else if (q && streq(changes[i].path, q)) + q = NULL; + else + assert_not_reached("wut?"); + } + assert(!p && !q); + unit_file_changes_free(changes, n_changes); + changes = NULL; n_changes = 0; + + assert_se(unit_file_enable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("linked3.service"), false, &changes, &n_changes) == -ELOOP); + unit_file_changes_free(changes, n_changes); + changes = NULL; n_changes = 0; +} + +static void test_default(const char *root) { + _cleanup_free_ char *def = NULL; + UnitFileChange *changes = NULL; + unsigned n_changes = 0; + const char *p; + + p = strjoina(root, "/usr/lib/systemd/system/test-default-real.target"); + assert_se(write_string_file(p, "# pretty much empty", WRITE_STRING_FILE_CREATE) >= 0); + + p = strjoina(root, "/usr/lib/systemd/system/test-default.target"); + assert_se(symlink("test-default-real.target", p) >= 0); + + assert_se(unit_file_get_default(UNIT_FILE_SYSTEM, root, &def) == -ENOENT); + + assert_se(unit_file_set_default(UNIT_FILE_SYSTEM, root, "idontexist.target", false, &changes, &n_changes) == -ENOENT); + assert_se(n_changes == 0); + unit_file_changes_free(changes, n_changes); + changes = NULL; n_changes = 0; + + assert_se(unit_file_get_default(UNIT_FILE_SYSTEM, root, &def) == -ENOENT); + + assert_se(unit_file_set_default(UNIT_FILE_SYSTEM, root, "test-default.target", false, &changes, &n_changes) >= 0); + assert_se(n_changes == 1); + assert_se(changes[0].type == UNIT_FILE_SYMLINK); + assert_se(streq(changes[0].source, "/usr/lib/systemd/system/test-default-real.target")); + p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/default.target"); + assert_se(streq(changes[0].path, p)); + unit_file_changes_free(changes, n_changes); + changes = NULL; n_changes = 0; + + assert_se(unit_file_get_default(UNIT_FILE_SYSTEM, root, &def) >= 0); + assert_se(streq_ptr(def, "test-default-real.target")); +} + +static void test_add_dependency(const char *root) { + UnitFileChange *changes = NULL; + unsigned n_changes = 0; + const char *p; + + p = strjoina(root, "/usr/lib/systemd/system/real-add-dependency-test-target.target"); + assert_se(write_string_file(p, "# pretty much empty", WRITE_STRING_FILE_CREATE) >= 0); + + p = strjoina(root, "/usr/lib/systemd/system/add-dependency-test-target.target"); + assert_se(symlink("real-add-dependency-test-target.target", p) >= 0); + + p = strjoina(root, "/usr/lib/systemd/system/real-add-dependency-test-service.service"); + assert_se(write_string_file(p, "# pretty much empty", WRITE_STRING_FILE_CREATE) >= 0); + + p = strjoina(root, "/usr/lib/systemd/system/add-dependency-test-service.service"); + assert_se(symlink("real-add-dependency-test-service.service", p) >= 0); + + assert_se(unit_file_add_dependency(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("add-dependency-test-service.service"), "add-dependency-test-target.target", UNIT_WANTS, false, &changes, &n_changes) >= 0); + assert_se(n_changes == 1); + assert_se(changes[0].type == UNIT_FILE_SYMLINK); + assert_se(streq(changes[0].source, "/usr/lib/systemd/system/real-add-dependency-test-service.service")); + p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/real-add-dependency-test-target.target.wants/real-add-dependency-test-service.service"); + assert_se(streq(changes[0].path, p)); + unit_file_changes_free(changes, n_changes); + changes = NULL; n_changes = 0; +} + +static void test_template_enable(const char *root) { + UnitFileChange *changes = NULL; + unsigned n_changes = 0; + UnitFileState state; + const char *p; + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@.service", &state) == -ENOENT); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@def.service", &state) == -ENOENT); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@foo.service", &state) == -ENOENT); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@foo.service", &state) == -ENOENT); + + p = strjoina(root, "/usr/lib/systemd/system/template@.service"); + assert_se(write_string_file(p, + "[Install]\n" + "DefaultInstance=def\n" + "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0); + + p = strjoina(root, "/usr/lib/systemd/system/template-symlink@.service"); + assert_se(symlink("template@.service", p) >= 0); + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + + assert_se(unit_file_enable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("template@.service"), false, &changes, &n_changes) >= 0); + assert_se(n_changes == 1); + assert_se(changes[0].type == UNIT_FILE_SYMLINK); + assert_se(streq(changes[0].source, "/usr/lib/systemd/system/template@.service")); + p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/template@def.service"); + assert_se(streq(changes[0].path, p)); + unit_file_changes_free(changes, n_changes); + changes = NULL; n_changes = 0; + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@def.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@def.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + + assert_se(unit_file_disable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("template@.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; + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + + assert_se(unit_file_enable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("template@foo.service"), false, &changes, &n_changes) >= 0); + assert_se(changes[0].type == UNIT_FILE_SYMLINK); + assert_se(streq(changes[0].source, "/usr/lib/systemd/system/template@.service")); + p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/template@foo.service"); + assert_se(streq(changes[0].path, p)); + unit_file_changes_free(changes, n_changes); + changes = NULL; n_changes = 0; + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@foo.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + + assert_se(unit_file_disable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("template@foo.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; + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@quux.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@quux.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + + assert_se(unit_file_enable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("template-symlink@quux.service"), false, &changes, &n_changes) >= 0); + assert_se(changes[0].type == UNIT_FILE_SYMLINK); + assert_se(streq(changes[0].source, "/usr/lib/systemd/system/template@.service")); + p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/template@quux.service"); + assert_se(streq(changes[0].path, p)); + unit_file_changes_free(changes, n_changes); + changes = NULL; n_changes = 0; + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@quux.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@quux.service", &state) >= 0 && state == UNIT_FILE_ENABLED); +} + +static void test_indirect(const char *root) { + UnitFileChange *changes = NULL; + unsigned n_changes = 0; + UnitFileState state; + const char *p; + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "indirecta.service", &state) == -ENOENT); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "indirectb.service", &state) == -ENOENT); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "indirectc.service", &state) == -ENOENT); + + p = strjoina(root, "/usr/lib/systemd/system/indirecta.service"); + assert_se(write_string_file(p, + "[Install]\n" + "Also=indirectb.service\n", WRITE_STRING_FILE_CREATE) >= 0); + + p = strjoina(root, "/usr/lib/systemd/system/indirectb.service"); + assert_se(write_string_file(p, + "[Install]\n" + "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0); + + p = strjoina(root, "/usr/lib/systemd/system/indirectc.service"); + assert_se(symlink("indirecta.service", p) >= 0); + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "indirecta.service", &state) >= 0 && state == UNIT_FILE_INDIRECT); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "indirectb.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "indirectc.service", &state) >= 0 && state == UNIT_FILE_INDIRECT); + + assert_se(unit_file_enable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("indirectc.service"), false, &changes, &n_changes) >= 0); + assert_se(n_changes == 1); + assert_se(changes[0].type == UNIT_FILE_SYMLINK); + assert_se(streq(changes[0].source, "/usr/lib/systemd/system/indirectb.service")); + p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/indirectb.service"); + assert_se(streq(changes[0].path, p)); + unit_file_changes_free(changes, n_changes); + changes = NULL; n_changes = 0; + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "indirecta.service", &state) >= 0 && state == UNIT_FILE_INDIRECT); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "indirectb.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "indirectc.service", &state) >= 0 && state == UNIT_FILE_INDIRECT); + + assert_se(unit_file_disable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("indirectc.service"), &changes, &n_changes) >= 0); + assert_se(n_changes == 1); + assert_se(changes[0].type == UNIT_FILE_UNLINK); + p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/indirectb.service"); + assert_se(streq(changes[0].path, p)); + unit_file_changes_free(changes, n_changes); + changes = NULL; n_changes = 0; +} + +static void test_preset_and_list(const char *root) { + UnitFileChange *changes = NULL; + unsigned n_changes = 0, i; + const char *p, *q; + UnitFileState state; + bool got_yes = false, got_no = false; + Iterator j; + UnitFileList *fl; + Hashmap *h; + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-yes.service", &state) == -ENOENT); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-no.service", &state) == -ENOENT); + + p = strjoina(root, "/usr/lib/systemd/system/preset-yes.service"); + assert_se(write_string_file(p, + "[Install]\n" + "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0); + + p = strjoina(root, "/usr/lib/systemd/system/preset-no.service"); + assert_se(write_string_file(p, + "[Install]\n" + "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0); + + p = strjoina(root, "/usr/lib/systemd/system-preset/test.preset"); + assert_se(write_string_file(p, + "enable *-yes.*\n" + "disable *\n", WRITE_STRING_FILE_CREATE) >= 0); + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-yes.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-no.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + + assert_se(unit_file_preset(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("preset-yes.service"), UNIT_FILE_PRESET_FULL, false, &changes, &n_changes) >= 0); + assert_se(n_changes == 1); + assert_se(changes[0].type == UNIT_FILE_SYMLINK); + assert_se(streq(changes[0].source, "/usr/lib/systemd/system/preset-yes.service")); + p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/preset-yes.service"); + assert_se(streq(changes[0].path, p)); + unit_file_changes_free(changes, n_changes); + changes = NULL; n_changes = 0; + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-yes.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-no.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + + assert_se(unit_file_disable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("preset-yes.service"), &changes, &n_changes) >= 0); + assert_se(n_changes == 1); + assert_se(changes[0].type == UNIT_FILE_UNLINK); + p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/preset-yes.service"); + assert_se(streq(changes[0].path, p)); + unit_file_changes_free(changes, n_changes); + changes = NULL; n_changes = 0; + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-yes.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-no.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + + assert_se(unit_file_preset(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("preset-no.service"), UNIT_FILE_PRESET_FULL, false, &changes, &n_changes) >= 0); + assert_se(n_changes == 0); + unit_file_changes_free(changes, n_changes); + changes = NULL; n_changes = 0; + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-yes.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-no.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + + assert_se(unit_file_preset_all(UNIT_FILE_SYSTEM, false, root, UNIT_FILE_PRESET_FULL, false, &changes, &n_changes) >= 0); + + assert_se(n_changes > 0); + + p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/preset-yes.service"); + + for (i = 0; i < n_changes; i++) { + + if (changes[i].type == UNIT_FILE_SYMLINK) { + assert_se(streq(changes[i].source, "/usr/lib/systemd/system/preset-yes.service")); + assert_se(streq(changes[i].path, p)); + } else + assert_se(changes[i].type == UNIT_FILE_UNLINK); + } + + unit_file_changes_free(changes, n_changes); + changes = NULL; n_changes = 0; + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-yes.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-no.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + + assert_se(h = hashmap_new(&string_hash_ops)); + assert_se(unit_file_get_list(UNIT_FILE_SYSTEM, root, h) >= 0); + + p = strjoina(root, "/usr/lib/systemd/system/preset-yes.service"); + q = strjoina(root, "/usr/lib/systemd/system/preset-no.service"); + + HASHMAP_FOREACH(fl, h, j) { + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, basename(fl->path), &state) >= 0); + assert_se(fl->state == state); + + if (streq(fl->path, p)) { + got_yes = true; + assert_se(fl->state == UNIT_FILE_ENABLED); + } else if (streq(fl->path, q)) { + got_no = true; + assert_se(fl->state == UNIT_FILE_DISABLED); + } else + assert_se(IN_SET(fl->state, UNIT_FILE_DISABLED, UNIT_FILE_STATIC, UNIT_FILE_INDIRECT)); + } + + unit_file_list_free(h); + + assert_se(got_yes && got_no); +} + +int main(int argc, char *argv[]) { + char root[] = "/tmp/rootXXXXXX"; + const char *p; + + assert_se(mkdtemp(root)); + + p = strjoina(root, "/usr/lib/systemd/system/"); + assert_se(mkdir_p(p, 0755) >= 0); + + p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/"); + assert_se(mkdir_p(p, 0755) >= 0); + + p = strjoina(root, "/run/systemd/system/"); + assert_se(mkdir_p(p, 0755) >= 0); + + p = strjoina(root, "/opt/"); + assert_se(mkdir_p(p, 0755) >= 0); + + p = strjoina(root, "/usr/lib/systemd/system-preset/"); + assert_se(mkdir_p(p, 0755) >= 0); + + test_basic_mask_and_enable(root); + test_linked_units(root); + test_default(root); + test_add_dependency(root); + test_template_enable(root); + test_indirect(root); + test_preset_and_list(root); + + assert_se(rm_rf(root, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0); + + return 0; +} diff --git a/src/test/test-install.c b/src/test/test-install.c index 5ee52e64cb..ef6f1efb89 100644 --- a/src/test/test-install.c +++ b/src/test/test-install.c @@ -19,8 +19,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <string.h> #include <stdio.h> +#include <string.h> #include "install.h" @@ -46,17 +46,19 @@ int main(int argc, char* argv[]) { const char *const files2[] = { "/home/lennart/test.service", NULL }; UnitFileChange *changes = NULL; unsigned n_changes = 0; + UnitFileState state = 0; h = hashmap_new(&string_hash_ops); r = unit_file_get_list(UNIT_FILE_SYSTEM, NULL, h); assert_se(r == 0); HASHMAP_FOREACH(p, h, i) { - UnitFileState s; + UnitFileState s = _UNIT_FILE_STATE_INVALID; - s = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(p->path)); + r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(p->path), &s); - assert_se(p->state == s); + assert_se((r < 0 && p->state == UNIT_FILE_BAD) || + (p->state == s)); fprintf(stderr, "%s (%s)\n", p->path, @@ -78,7 +80,9 @@ int main(int argc, char* argv[]) { dump_changes(changes, n_changes); unit_file_changes_free(changes, n_changes); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0]) == UNIT_FILE_ENABLED); + r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0], &state); + assert_se(r >= 0); + assert_se(state == UNIT_FILE_ENABLED); log_error("disable"); @@ -91,7 +95,9 @@ int main(int argc, char* argv[]) { dump_changes(changes, n_changes); unit_file_changes_free(changes, n_changes); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0]) == UNIT_FILE_DISABLED); + r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0], &state); + assert_se(r >= 0); + assert_se(state == UNIT_FILE_DISABLED); log_error("mask"); changes = NULL; @@ -106,7 +112,9 @@ int main(int argc, char* argv[]) { dump_changes(changes, n_changes); unit_file_changes_free(changes, n_changes); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0]) == UNIT_FILE_MASKED); + r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0], &state); + assert_se(r >= 0); + assert_se(state == UNIT_FILE_MASKED); log_error("unmask"); changes = NULL; @@ -121,7 +129,9 @@ int main(int argc, char* argv[]) { dump_changes(changes, n_changes); unit_file_changes_free(changes, n_changes); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0]) == UNIT_FILE_DISABLED); + r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0], &state); + assert_se(r >= 0); + assert_se(state == UNIT_FILE_DISABLED); log_error("mask"); changes = NULL; @@ -133,7 +143,9 @@ int main(int argc, char* argv[]) { dump_changes(changes, n_changes); unit_file_changes_free(changes, n_changes); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0]) == UNIT_FILE_MASKED); + r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0], &state); + assert_se(r >= 0); + assert_se(state == UNIT_FILE_MASKED); log_error("disable"); changes = NULL; @@ -148,7 +160,9 @@ int main(int argc, char* argv[]) { dump_changes(changes, n_changes); unit_file_changes_free(changes, n_changes); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0]) == UNIT_FILE_MASKED); + r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0], &state); + assert_se(r >= 0); + assert_se(state == UNIT_FILE_MASKED); log_error("umask"); changes = NULL; @@ -160,7 +174,9 @@ int main(int argc, char* argv[]) { dump_changes(changes, n_changes); unit_file_changes_free(changes, n_changes); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0]) == UNIT_FILE_DISABLED); + r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0], &state); + assert_se(r >= 0); + assert_se(state == UNIT_FILE_DISABLED); log_error("enable files2"); changes = NULL; @@ -172,19 +188,22 @@ int main(int argc, char* argv[]) { dump_changes(changes, n_changes); unit_file_changes_free(changes, n_changes); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0])) == UNIT_FILE_ENABLED); + r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0]), &state); + assert_se(r >= 0); + assert_se(state == UNIT_FILE_ENABLED); log_error("disable files2"); changes = NULL; n_changes = 0; - r = unit_file_disable(UNIT_FILE_SYSTEM, false, NULL, (char**) files2, &changes, &n_changes); + r = unit_file_disable(UNIT_FILE_SYSTEM, false, NULL, STRV_MAKE(basename(files2[0])), &changes, &n_changes); assert_se(r >= 0); dump_changes(changes, n_changes); unit_file_changes_free(changes, n_changes); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0])) == _UNIT_FILE_STATE_INVALID); + r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0]), &state); + assert_se(r < 0); log_error("link files2"); changes = NULL; @@ -196,19 +215,22 @@ int main(int argc, char* argv[]) { dump_changes(changes, n_changes); unit_file_changes_free(changes, n_changes); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0])) == UNIT_FILE_LINKED); + r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0]), &state); + assert_se(r >= 0); + assert_se(state == UNIT_FILE_LINKED); log_error("disable files2"); changes = NULL; n_changes = 0; - r = unit_file_disable(UNIT_FILE_SYSTEM, false, NULL, (char**) files2, &changes, &n_changes); + r = unit_file_disable(UNIT_FILE_SYSTEM, false, NULL, STRV_MAKE(basename(files2[0])), &changes, &n_changes); assert_se(r >= 0); dump_changes(changes, n_changes); unit_file_changes_free(changes, n_changes); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0])) == _UNIT_FILE_STATE_INVALID); + r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0]), &state); + assert_se(r < 0); log_error("link files2"); changes = NULL; @@ -220,7 +242,9 @@ int main(int argc, char* argv[]) { dump_changes(changes, n_changes); unit_file_changes_free(changes, n_changes); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0])) == UNIT_FILE_LINKED); + r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0]), &state); + assert_se(r >= 0); + assert_se(state == UNIT_FILE_LINKED); log_error("reenable files2"); changes = NULL; @@ -232,19 +256,22 @@ int main(int argc, char* argv[]) { dump_changes(changes, n_changes); unit_file_changes_free(changes, n_changes); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0])) == UNIT_FILE_ENABLED); + r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0]), &state); + assert_se(r >= 0); + assert_se(state == UNIT_FILE_ENABLED); log_error("disable files2"); changes = NULL; n_changes = 0; - r = unit_file_disable(UNIT_FILE_SYSTEM, false, NULL, (char**) files2, &changes, &n_changes); + r = unit_file_disable(UNIT_FILE_SYSTEM, false, NULL, STRV_MAKE(basename(files2[0])), &changes, &n_changes); assert_se(r >= 0); dump_changes(changes, n_changes); unit_file_changes_free(changes, n_changes); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0])) == _UNIT_FILE_STATE_INVALID); + r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0]), &state); + assert_se(r < 0); log_error("preset files"); changes = NULL; n_changes = 0; @@ -255,7 +282,9 @@ int main(int argc, char* argv[]) { dump_changes(changes, n_changes); unit_file_changes_free(changes, n_changes); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files[0])) == UNIT_FILE_ENABLED); + r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files[0]), &state); + assert_se(r >= 0); + assert_se(state == UNIT_FILE_ENABLED); return 0; } diff --git a/src/test/test-job-type.c b/src/test/test-job-type.c index af0d76e894..75ce3a349e 100644 --- a/src/test/test-job-type.c +++ b/src/test/test-job-type.c @@ -22,8 +22,8 @@ #include <stdio.h> #include "job.h" -#include "unit.h" #include "service.h" +#include "unit.h" int main(int argc, char*argv[]) { JobType a, b, c, ab, bc, ab_c, bc_a, a_bc; diff --git a/src/test/test-locale-util.c b/src/test/test-locale-util.c index 9765075365..427c698d1d 100644 --- a/src/test/test-locale-util.c +++ b/src/test/test-locale-util.c @@ -19,8 +19,8 @@ #include "locale-util.h" -#include "strv.h" #include "macro.h" +#include "strv.h" static void test_get_locales(void) { _cleanup_strv_free_ char **locales = NULL; diff --git a/src/test/test-log.c b/src/test/test-log.c index 9dcfa2f274..a01df9b049 100644 --- a/src/test/test-log.c +++ b/src/test/test-log.c @@ -22,9 +22,9 @@ #include <stddef.h> #include <unistd.h> +#include "formats-util.h" #include "log.h" #include "util.h" -#include "formats-util.h" int main(int argc, char* argv[]) { diff --git a/src/test/test-loopback.c b/src/test/test-loopback.c index e3e5a95add..556938a0f8 100644 --- a/src/test/test-loopback.c +++ b/src/test/test-loopback.c @@ -19,11 +19,11 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <string.h> #include <stdio.h> +#include <string.h> -#include "loopback-setup.h" #include "log.h" +#include "loopback-setup.h" int main(int argc, char* argv[]) { int r; diff --git a/src/test/test-ns.c b/src/test/test-ns.c index 3050be9e9d..1175114a3a 100644 --- a/src/test/test-ns.c +++ b/src/test/test-ns.c @@ -22,8 +22,8 @@ #include <stdlib.h> #include <unistd.h> -#include "namespace.h" #include "log.h" +#include "namespace.h" int main(int argc, char *argv[]) { const char * const writable[] = { diff --git a/src/test/test-parse-util.c b/src/test/test-parse-util.c index d3ae0599ab..f0d5d71083 100644 --- a/src/test/test-parse-util.c +++ b/src/test/test-parse-util.c @@ -352,6 +352,24 @@ static void test_safe_atolli(void) { assert_se(r == 0); assert_se(l == 12345); + r = safe_atolli(" 12345", &l); + assert_se(r == 0); + assert_se(l == 12345); + + r = safe_atolli("-12345", &l); + assert_se(r == 0); + assert_se(l == -12345); + + r = safe_atolli(" -12345", &l); + assert_se(r == 0); + assert_se(l == -12345); + + r = safe_atolli("12345678901234567890", &l); + assert_se(r == -ERANGE); + + r = safe_atolli("-12345678901234567890", &l); + assert_se(r == -ERANGE); + r = safe_atolli("junk", &l); assert_se(r == -EINVAL); } @@ -364,9 +382,19 @@ static void test_safe_atou16(void) { assert_se(r == 0); assert_se(l == 12345); + r = safe_atou16(" 12345", &l); + assert_se(r == 0); + assert_se(l == 12345); + r = safe_atou16("123456", &l); assert_se(r == -ERANGE); + r = safe_atou16("-1", &l); + assert_se(r == -ERANGE); + + r = safe_atou16(" -1", &l); + assert_se(r == -ERANGE); + r = safe_atou16("junk", &l); assert_se(r == -EINVAL); } @@ -379,9 +407,24 @@ static void test_safe_atoi16(void) { assert_se(r == 0); assert_se(l == -12345); + r = safe_atoi16(" -12345", &l); + assert_se(r == 0); + assert_se(l == -12345); + + r = safe_atoi16("32767", &l); + assert_se(r == 0); + assert_se(l == 32767); + + r = safe_atoi16(" 32767", &l); + assert_se(r == 0); + assert_se(l == 32767); + r = safe_atoi16("36536", &l); assert_se(r == -ERANGE); + r = safe_atoi16("-32769", &l); + assert_se(r == -ERANGE); + r = safe_atoi16("junk", &l); assert_se(r == -EINVAL); } diff --git a/src/test/test-ratelimit.c b/src/test/test-ratelimit.c index 462b55cdb3..990b834c79 100644 --- a/src/test/test-ratelimit.c +++ b/src/test/test-ratelimit.c @@ -19,9 +19,9 @@ #include <unistd.h> +#include "macro.h" #include "ratelimit.h" #include "time-util.h" -#include "macro.h" static void test_ratelimit_test(void) { int i; diff --git a/src/test/test-sched-prio.c b/src/test/test-sched-prio.c index ebc9110c4d..8396ae60f3 100644 --- a/src/test/test-sched-prio.c +++ b/src/test/test-sched-prio.c @@ -21,8 +21,8 @@ #include <sched.h> -#include "manager.h" #include "macro.h" +#include "manager.h" int main(int argc, char *argv[]) { Manager *m = NULL; diff --git a/src/test/test-siphash24.c b/src/test/test-siphash24.c index 2402da6a6f..c20be99350 100644 --- a/src/test/test-siphash24.c +++ b/src/test/test-siphash24.c @@ -19,23 +19,18 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include "util.h" #include "siphash24.h" +#include "util.h" #define ITERATIONS 10000000ULL -/* see https://131002.net/siphash/siphash.pdf, Appendix A */ -int main(int argc, char *argv[]) { +static int do_test(const uint8_t *in, size_t len, const uint8_t *key) { struct siphash state = {}; - const uint8_t in[15] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e }; - const uint8_t key[16] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}; uint64_t out = 0; unsigned i, j; - siphash24((uint8_t *)&out, in, sizeof(in), key); - assert_se(out == htole64(0xa129ca6149be45e5)); + out = siphash24(in, len, key); + assert_se(out == 0xa129ca6149be45e5); /* verify the internal state as given in the above paper */ siphash24_init(&state, key); @@ -43,13 +38,13 @@ int main(int argc, char *argv[]) { assert_se(state.v1 == 0x6b617f6d656e6665); assert_se(state.v2 == 0x6b7f62616d677361); assert_se(state.v3 == 0x7b6b696e727e6c7b); - siphash24_compress(in, sizeof(in), &state); + siphash24_compress(in, len, &state); assert_se(state.v0 == 0x4a017198de0a59e0); assert_se(state.v1 == 0x0d52f6f62a4f59a4); assert_se(state.v2 == 0x634cb3577b01fd3d); assert_se(state.v3 == 0xa5224d6f55c7d9c8); - siphash24_finalize((uint8_t*)&out, &state); - assert_se(out == htole64(0xa129ca6149be45e5)); + out = siphash24_finalize(&state); + assert_se(out == 0xa129ca6149be45e5); assert_se(state.v0 == 0xf6bcd53893fecff1); assert_se(state.v1 == 0x54b9964c7ea0d937); assert_se(state.v2 == 0x1b38329c099bb55a); @@ -57,14 +52,34 @@ int main(int argc, char *argv[]) { /* verify that decomposing the input in three chunks gives the same result */ - for (i = 0; i < sizeof(in); i++) { - for (j = i; j < sizeof(in); j++) { + for (i = 0; i < len; i++) { + for (j = i; j < len; j++) { siphash24_init(&state, key); siphash24_compress(in, i, &state); siphash24_compress(&in[i], j - i, &state); - siphash24_compress(&in[j], sizeof(in) - j, &state); - siphash24_finalize((uint8_t*)&out, &state); - assert_se(out == htole64(0xa129ca6149be45e5)); + siphash24_compress(&in[j], len - j, &state); + out = siphash24_finalize(&state); + assert_se(out == 0xa129ca6149be45e5); } } + return 0; +} + +/* see https://131002.net/siphash/siphash.pdf, Appendix A */ +int main(int argc, char *argv[]) { + const uint8_t in[15] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e }; + const uint8_t key[16] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}; + uint8_t in_buf[20]; + + /* Test with same input but different alignments. */ + memcpy(in_buf, in, sizeof(in)); + do_test(in_buf, sizeof(in), key); + memcpy(in_buf + 1, in, sizeof(in)); + do_test(in_buf + 1, sizeof(in), key); + memcpy(in_buf + 2, in, sizeof(in)); + do_test(in_buf + 2, sizeof(in), key); + memcpy(in_buf + 4, in, sizeof(in)); + do_test(in_buf + 4, sizeof(in), key); } diff --git a/src/test/test-sleep.c b/src/test/test-sleep.c index 4308ddfb64..fb115ce4f3 100644 --- a/src/test/test-sleep.c +++ b/src/test/test-sleep.c @@ -21,10 +21,10 @@ #include <stdio.h> -#include "util.h" #include "log.h" #include "sleep-config.h" #include "strv.h" +#include "util.h" static void test_sleep(void) { _cleanup_strv_free_ char diff --git a/src/test/test-socket-util.c b/src/test/test-socket-util.c index b9699b2028..33ff3755bc 100644 --- a/src/test/test-socket-util.c +++ b/src/test/test-socket-util.c @@ -41,28 +41,25 @@ static void test_socket_address_parse(void) { assert_se(socket_address_parse(&a, "65535") >= 0); - if (socket_ipv6_is_supported()) { - assert_se(socket_address_parse(&a, "[::1]") < 0); - assert_se(socket_address_parse(&a, "[::1]8888") < 0); - assert_se(socket_address_parse(&a, "::1") < 0); - assert_se(socket_address_parse(&a, "[::1]:0") < 0); - assert_se(socket_address_parse(&a, "[::1]:65536") < 0); - assert_se(socket_address_parse(&a, "[a:b:1]:8888") < 0); - - assert_se(socket_address_parse(&a, "8888") >= 0); - assert_se(a.sockaddr.sa.sa_family == AF_INET6); - - assert_se(socket_address_parse(&a, "[2001:0db8:0000:85a3:0000:0000:ac1f:8001]:8888") >= 0); - assert_se(a.sockaddr.sa.sa_family == AF_INET6); - - assert_se(socket_address_parse(&a, "[::1]:8888") >= 0); - assert_se(a.sockaddr.sa.sa_family == AF_INET6); - } else { - assert_se(socket_address_parse(&a, "[::1]:8888") < 0); - - assert_se(socket_address_parse(&a, "8888") >= 0); - assert_se(a.sockaddr.sa.sa_family == AF_INET); - } + /* The checks below will pass even if ipv6 is disabled in + * kernel. The underlying glibc's inet_pton() is just a string + * parser and doesn't make any syscalls. */ + + assert_se(socket_address_parse(&a, "[::1]") < 0); + assert_se(socket_address_parse(&a, "[::1]8888") < 0); + assert_se(socket_address_parse(&a, "::1") < 0); + assert_se(socket_address_parse(&a, "[::1]:0") < 0); + assert_se(socket_address_parse(&a, "[::1]:65536") < 0); + assert_se(socket_address_parse(&a, "[a:b:1]:8888") < 0); + + assert_se(socket_address_parse(&a, "8888") >= 0); + assert_se(a.sockaddr.sa.sa_family == (socket_ipv6_is_supported() ? AF_INET6 : AF_INET)); + + assert_se(socket_address_parse(&a, "[2001:0db8:0000:85a3:0000:0000:ac1f:8001]:8888") >= 0); + assert_se(a.sockaddr.sa.sa_family == AF_INET6); + + assert_se(socket_address_parse(&a, "[::1]:8888") >= 0); + assert_se(a.sockaddr.sa.sa_family == AF_INET6); assert_se(socket_address_parse(&a, "192.168.1.254:8888") >= 0); assert_se(a.sockaddr.sa.sa_family == AF_INET); diff --git a/src/test/test-tables.c b/src/test/test-tables.c index ed4abdbf12..aef992ee3c 100644 --- a/src/test/test-tables.c +++ b/src/test/test-tables.c @@ -36,21 +36,19 @@ #include "logs-show.h" #include "mount.h" #include "path.h" +#include "rlimit-util.h" #include "scope.h" #include "service.h" #include "slice.h" -#include "snapshot.h" #include "socket-util.h" #include "socket.h" #include "swap.h" #include "target.h" +#include "test-tables.h" #include "timer.h" #include "unit-name.h" #include "unit.h" #include "util.h" -#include "rlimit-util.h" - -#include "test-tables.h" int main(int argc, char **argv) { test_table(architecture, ARCHITECTURE); @@ -98,7 +96,6 @@ int main(int argc, char **argv) { test_table(service_state, SERVICE_STATE); test_table(service_type, SERVICE_TYPE); test_table(slice_state, SLICE_STATE); - test_table(snapshot_state, SNAPSHOT_STATE); test_table(socket_address_bind_ipv6_only, SOCKET_ADDRESS_BIND_IPV6_ONLY); test_table(socket_exec_command, SOCKET_EXEC_COMMAND); test_table(socket_result, SOCKET_RESULT); diff --git a/src/test/test-terminal-util.c b/src/test/test-terminal-util.c index e940b5a204..84b448a095 100644 --- a/src/test/test-terminal-util.c +++ b/src/test/test-terminal-util.c @@ -18,8 +18,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <stdio.h> #include <stdbool.h> +#include <stdio.h> #include "fd-util.h" #include "fileio.h" diff --git a/src/test/test-time.c b/src/test/test-time.c index 820e4aaee2..8896b2c92b 100644 --- a/src/test/test-time.c +++ b/src/test/test-time.c @@ -19,8 +19,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include "time-util.h" #include "strv.h" +#include "time-util.h" static void test_parse_sec(void) { usec_t u; diff --git a/src/test/test-unaligned.c b/src/test/test-unaligned.c index 1754d06b2d..b18b3fca0e 100644 --- a/src/test/test-unaligned.c +++ b/src/test/test-unaligned.c @@ -17,8 +17,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include "unaligned.h" #include "sparse-endian.h" +#include "unaligned.h" #include "util.h" static uint8_t data[] = { @@ -26,7 +26,7 @@ static uint8_t data[] = { 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, }; -int main(int argc, const char *argv[]) { +static void test_be(void) { uint8_t scratch[16]; assert_se(unaligned_read_be16(&data[0]) == 0x0001); @@ -91,3 +91,75 @@ int main(int argc, const char *argv[]) { unaligned_write_be64(&scratch[7], 0x0708090a0b0c0d0e); assert_se(memcmp(&scratch[7], &data[7], sizeof(uint64_t)) == 0); } + +static void test_le(void) { + uint8_t scratch[16]; + + assert_se(unaligned_read_le16(&data[0]) == 0x0100); + assert_se(unaligned_read_le16(&data[1]) == 0x0201); + + assert_se(unaligned_read_le32(&data[0]) == 0x03020100); + assert_se(unaligned_read_le32(&data[1]) == 0x04030201); + assert_se(unaligned_read_le32(&data[2]) == 0x05040302); + assert_se(unaligned_read_le32(&data[3]) == 0x06050403); + + assert_se(unaligned_read_le64(&data[0]) == 0x0706050403020100); + assert_se(unaligned_read_le64(&data[1]) == 0x0807060504030201); + assert_se(unaligned_read_le64(&data[2]) == 0x0908070605040302); + assert_se(unaligned_read_le64(&data[3]) == 0x0a09080706050403); + assert_se(unaligned_read_le64(&data[4]) == 0x0b0a090807060504); + assert_se(unaligned_read_le64(&data[5]) == 0x0c0b0a0908070605); + assert_se(unaligned_read_le64(&data[6]) == 0x0d0c0b0a09080706); + assert_se(unaligned_read_le64(&data[7]) == 0x0e0d0c0b0a090807); + + zero(scratch); + unaligned_write_le16(&scratch[0], 0x0100); + assert_se(memcmp(&scratch[0], &data[0], sizeof(uint16_t)) == 0); + zero(scratch); + unaligned_write_le16(&scratch[1], 0x0201); + assert_se(memcmp(&scratch[1], &data[1], sizeof(uint16_t)) == 0); + + zero(scratch); + unaligned_write_le32(&scratch[0], 0x03020100); + + assert_se(memcmp(&scratch[0], &data[0], sizeof(uint32_t)) == 0); + zero(scratch); + unaligned_write_le32(&scratch[1], 0x04030201); + assert_se(memcmp(&scratch[1], &data[1], sizeof(uint32_t)) == 0); + zero(scratch); + unaligned_write_le32(&scratch[2], 0x05040302); + assert_se(memcmp(&scratch[2], &data[2], sizeof(uint32_t)) == 0); + zero(scratch); + unaligned_write_le32(&scratch[3], 0x06050403); + assert_se(memcmp(&scratch[3], &data[3], sizeof(uint32_t)) == 0); + + zero(scratch); + unaligned_write_le64(&scratch[0], 0x0706050403020100); + assert_se(memcmp(&scratch[0], &data[0], sizeof(uint64_t)) == 0); + zero(scratch); + unaligned_write_le64(&scratch[1], 0x0807060504030201); + assert_se(memcmp(&scratch[1], &data[1], sizeof(uint64_t)) == 0); + zero(scratch); + unaligned_write_le64(&scratch[2], 0x0908070605040302); + assert_se(memcmp(&scratch[2], &data[2], sizeof(uint64_t)) == 0); + zero(scratch); + unaligned_write_le64(&scratch[3], 0x0a09080706050403); + assert_se(memcmp(&scratch[3], &data[3], sizeof(uint64_t)) == 0); + zero(scratch); + unaligned_write_le64(&scratch[4], 0x0B0A090807060504); + assert_se(memcmp(&scratch[4], &data[4], sizeof(uint64_t)) == 0); + zero(scratch); + unaligned_write_le64(&scratch[5], 0x0c0b0a0908070605); + assert_se(memcmp(&scratch[5], &data[5], sizeof(uint64_t)) == 0); + zero(scratch); + unaligned_write_le64(&scratch[6], 0x0d0c0b0a09080706); + assert_se(memcmp(&scratch[6], &data[6], sizeof(uint64_t)) == 0); + zero(scratch); + unaligned_write_le64(&scratch[7], 0x0e0d0c0b0a090807); + assert_se(memcmp(&scratch[7], &data[7], sizeof(uint64_t)) == 0); +} + +int main(int argc, const char *argv[]) { + test_be(); + test_le(); +} diff --git a/src/test/test-unit-file.c b/src/test/test-unit-file.c index 3648ec9c58..0b3630f77c 100644 --- a/src/test/test-unit-file.c +++ b/src/test/test-unit-file.c @@ -40,6 +40,7 @@ #include "string-util.h" #include "strv.h" #include "test-helper.h" +#include "user-util.h" #include "util.h" static int test_unit_file_get_set(void) { @@ -558,76 +559,66 @@ static void test_load_env_file_5(void) { static void test_install_printf(void) { char name[] = "name.service", - path[] = "/run/systemd/system/name.service", - user[] = "xxxx-no-such-user"; - UnitFileInstallInfo i = {name, path, user}; - UnitFileInstallInfo i2 = {name, path, NULL}; + path[] = "/run/systemd/system/name.service"; + UnitFileInstallInfo i = { .name = name, .path = path, }; + UnitFileInstallInfo i2 = { .name= name, .path = path, }; char name3[] = "name@inst.service", path3[] = "/run/systemd/system/name.service"; - UnitFileInstallInfo i3 = {name3, path3, user}; - UnitFileInstallInfo i4 = {name3, path3, NULL}; + UnitFileInstallInfo i3 = { .name = name3, .path = path3, }; + UnitFileInstallInfo i4 = { .name = name3, .path = path3, }; - _cleanup_free_ char *mid, *bid, *host; + _cleanup_free_ char *mid = NULL, *bid = NULL, *host = NULL, *uid = NULL, *user = NULL; assert_se(specifier_machine_id('m', NULL, NULL, &mid) >= 0 && mid); assert_se(specifier_boot_id('b', NULL, NULL, &bid) >= 0 && bid); assert_se((host = gethostname_malloc())); + assert_se((user = getusername_malloc())); + assert_se(asprintf(&uid, UID_FMT, getuid()) >= 0); #define expect(src, pattern, result) \ do { \ _cleanup_free_ char *t = NULL; \ _cleanup_free_ char \ *d1 = strdup(i.name), \ - *d2 = strdup(i.path), \ - *d3 = strdup(i.user); \ + *d2 = strdup(i.path); \ assert_se(install_full_printf(&src, pattern, &t) >= 0 || !result); \ memzero(i.name, strlen(i.name)); \ memzero(i.path, strlen(i.path)); \ - memzero(i.user, strlen(i.user)); \ - assert_se(d1 && d2 && d3); \ + assert_se(d1 && d2); \ if (result) { \ printf("%s\n", t); \ - assert_se(streq(t, result)); \ - } else assert_se(t == NULL); \ + assert_se(streq(t, result)); \ + } else assert_se(t == NULL); \ strcpy(i.name, d1); \ strcpy(i.path, d2); \ - strcpy(i.user, d3); \ } while(false) - assert_se(setenv("USER", "root", 1) == 0); - expect(i, "%n", "name.service"); expect(i, "%N", "name"); expect(i, "%p", "name"); expect(i, "%i", ""); - expect(i, "%u", "xxxx-no-such-user"); - - DISABLE_WARNING_NONNULL; - expect(i, "%U", NULL); - REENABLE_WARNING; + expect(i, "%u", user); + expect(i, "%U", uid); expect(i, "%m", mid); expect(i, "%b", bid); expect(i, "%H", host); - expect(i2, "%u", "root"); - expect(i2, "%U", "0"); + expect(i2, "%u", user); + expect(i2, "%U", uid); expect(i3, "%n", "name@inst.service"); expect(i3, "%N", "name@inst"); expect(i3, "%p", "name"); - expect(i3, "%u", "xxxx-no-such-user"); - - DISABLE_WARNING_NONNULL; - expect(i3, "%U", NULL); - REENABLE_WARNING; + expect(i3, "%u", user); + expect(i3, "%U", uid); expect(i3, "%m", mid); expect(i3, "%b", bid); expect(i3, "%H", host); - expect(i4, "%u", "root"); - expect(i4, "%U", "0"); + expect(i4, "%u", user); + expect(i4, "%U", uid); } static uint64_t make_cap(int cap) { @@ -689,11 +680,42 @@ static void test_config_parse_rlimit(void) { assert_se(rl[RLIMIT_NOFILE]->rlim_cur == 55); assert_se(rl[RLIMIT_NOFILE]->rlim_cur == rl[RLIMIT_NOFILE]->rlim_max); + assert_se(config_parse_limit(NULL, "fake", 1, "section", 1, "LimitNOFILE", RLIMIT_NOFILE, "55:66", rl, NULL) >= 0); + assert_se(rl[RLIMIT_NOFILE]); + assert_se(rl[RLIMIT_NOFILE]->rlim_cur == 55); + assert_se(rl[RLIMIT_NOFILE]->rlim_max == 66); + assert_se(config_parse_limit(NULL, "fake", 1, "section", 1, "LimitNOFILE", RLIMIT_NOFILE, "infinity", rl, NULL) >= 0); assert_se(rl[RLIMIT_NOFILE]); assert_se(rl[RLIMIT_NOFILE]->rlim_cur == RLIM_INFINITY); assert_se(rl[RLIMIT_NOFILE]->rlim_cur == rl[RLIMIT_NOFILE]->rlim_max); + assert_se(config_parse_limit(NULL, "fake", 1, "section", 1, "LimitNOFILE", RLIMIT_NOFILE, "infinity:infinity", rl, NULL) >= 0); + assert_se(rl[RLIMIT_NOFILE]); + assert_se(rl[RLIMIT_NOFILE]->rlim_cur == RLIM_INFINITY); + assert_se(rl[RLIMIT_NOFILE]->rlim_cur == rl[RLIMIT_NOFILE]->rlim_max); + + assert_se(config_parse_limit(NULL, "fake", 1, "section", 1, "LimitNOFILE", RLIMIT_NOFILE, "10:20:30", rl, NULL) >= 0); + assert_se(rl[RLIMIT_NOFILE]); + assert_se(rl[RLIMIT_NOFILE]->rlim_cur == 10); + assert_se(rl[RLIMIT_NOFILE]->rlim_max == 20); + + /* Invalid values don't change rl */ + assert_se(config_parse_limit(NULL, "fake", 1, "section", 1, "LimitNOFILE", RLIMIT_NOFILE, "wat:wat", rl, NULL) >= 0); + assert_se(rl[RLIMIT_NOFILE]); + assert_se(rl[RLIMIT_NOFILE]->rlim_cur == 10); + assert_se(rl[RLIMIT_NOFILE]->rlim_max == 20); + + assert_se(config_parse_limit(NULL, "fake", 1, "section", 1, "LimitNOFILE", RLIMIT_NOFILE, "66:wat", rl, NULL) >= 0); + assert_se(rl[RLIMIT_NOFILE]); + assert_se(rl[RLIMIT_NOFILE]->rlim_cur == 10); + assert_se(rl[RLIMIT_NOFILE]->rlim_max == 20); + + assert_se(config_parse_limit(NULL, "fake", 1, "section", 1, "LimitNOFILE", RLIMIT_NOFILE, "200:100", rl, NULL) >= 0); + assert_se(rl[RLIMIT_NOFILE]); + assert_se(rl[RLIMIT_NOFILE]->rlim_cur == 10); + assert_se(rl[RLIMIT_NOFILE]->rlim_max == 20); + rl[RLIMIT_NOFILE] = mfree(rl[RLIMIT_NOFILE]); assert_se(config_parse_sec_limit(NULL, "fake", 1, "section", 1, "LimitCPU", RLIMIT_CPU, "56", rl, NULL) >= 0); @@ -706,6 +728,11 @@ static void test_config_parse_rlimit(void) { assert_se(rl[RLIMIT_CPU]->rlim_cur == 57); assert_se(rl[RLIMIT_CPU]->rlim_cur == rl[RLIMIT_CPU]->rlim_max); + assert_se(config_parse_sec_limit(NULL, "fake", 1, "section", 1, "LimitCPU", RLIMIT_CPU, "40s:1m", rl, NULL) >= 0); + assert_se(rl[RLIMIT_CPU]); + assert_se(rl[RLIMIT_CPU]->rlim_cur == 40); + assert_se(rl[RLIMIT_CPU]->rlim_max == 60); + assert_se(config_parse_sec_limit(NULL, "fake", 1, "section", 1, "LimitCPU", RLIMIT_CPU, "infinity", rl, NULL) >= 0); assert_se(rl[RLIMIT_CPU]); assert_se(rl[RLIMIT_CPU]->rlim_cur == RLIM_INFINITY); @@ -723,16 +750,31 @@ static void test_config_parse_rlimit(void) { assert_se(rl[RLIMIT_RTTIME]->rlim_cur == 58); assert_se(rl[RLIMIT_RTTIME]->rlim_cur == rl[RLIMIT_RTTIME]->rlim_max); + assert_se(config_parse_usec_limit(NULL, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME, "58:60", rl, NULL) >= 0); + assert_se(rl[RLIMIT_RTTIME]); + assert_se(rl[RLIMIT_RTTIME]->rlim_cur == 58); + assert_se(rl[RLIMIT_RTTIME]->rlim_max == 60); + assert_se(config_parse_usec_limit(NULL, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME, "59s", rl, NULL) >= 0); assert_se(rl[RLIMIT_RTTIME]); assert_se(rl[RLIMIT_RTTIME]->rlim_cur == 59 * USEC_PER_SEC); assert_se(rl[RLIMIT_RTTIME]->rlim_cur == rl[RLIMIT_RTTIME]->rlim_max); + assert_se(config_parse_usec_limit(NULL, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME, "59s:123s", rl, NULL) >= 0); + assert_se(rl[RLIMIT_RTTIME]); + assert_se(rl[RLIMIT_RTTIME]->rlim_cur == 59 * USEC_PER_SEC); + assert_se(rl[RLIMIT_RTTIME]->rlim_max == 123 * USEC_PER_SEC); + assert_se(config_parse_usec_limit(NULL, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME, "infinity", rl, NULL) >= 0); assert_se(rl[RLIMIT_RTTIME]); assert_se(rl[RLIMIT_RTTIME]->rlim_cur == RLIM_INFINITY); assert_se(rl[RLIMIT_RTTIME]->rlim_cur == rl[RLIMIT_RTTIME]->rlim_max); + assert_se(config_parse_usec_limit(NULL, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME, "infinity:infinity", rl, NULL) >= 0); + assert_se(rl[RLIMIT_RTTIME]); + assert_se(rl[RLIMIT_RTTIME]->rlim_cur == RLIM_INFINITY); + assert_se(rl[RLIMIT_RTTIME]->rlim_cur == rl[RLIMIT_RTTIME]->rlim_max); + assert_se(config_parse_usec_limit(NULL, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME, "2345ms", rl, NULL) >= 0); assert_se(rl[RLIMIT_RTTIME]); assert_se(rl[RLIMIT_RTTIME]->rlim_cur == 2345 * USEC_PER_MSEC); @@ -741,6 +783,44 @@ static void test_config_parse_rlimit(void) { rl[RLIMIT_RTTIME] = mfree(rl[RLIMIT_RTTIME]); } +static void test_config_parse_pass_environ(void) { + /* int config_parse_pass_environ( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) */ + int r; + _cleanup_strv_free_ char **passenv = NULL; + + r = config_parse_pass_environ(NULL, "fake", 1, "section", 1, + "PassEnvironment", 0, "A B", + &passenv, NULL); + assert_se(r >= 0); + assert_se(strv_length(passenv) == 2); + assert_se(streq(passenv[0], "A")); + assert_se(streq(passenv[1], "B")); + + r = config_parse_pass_environ(NULL, "fake", 1, "section", 1, + "PassEnvironment", 0, "", + &passenv, NULL); + assert_se(r >= 0); + assert_se(strv_isempty(passenv)); + + r = config_parse_pass_environ(NULL, "fake", 1, "section", 1, + "PassEnvironment", 0, "'invalid name' 'normal_name' A=1 \\", + &passenv, NULL); + assert_se(r >= 0); + assert_se(strv_length(passenv) == 1); + assert_se(streq(passenv[0], "normal_name")); + +} + int main(int argc, char *argv[]) { int r; @@ -751,6 +831,7 @@ int main(int argc, char *argv[]) { test_config_parse_exec(); test_config_parse_bounding_set(); test_config_parse_rlimit(); + test_config_parse_pass_environ(); test_load_env_file_1(); test_load_env_file_2(); test_load_env_file_3(); diff --git a/src/test/test-unit-name.c b/src/test/test-unit-name.c index 9db7853dd4..842ca40102 100644 --- a/src/test/test-unit-name.c +++ b/src/test/test-unit-name.c @@ -37,6 +37,7 @@ #include "unit-name.h" #include "unit-printf.h" #include "unit.h" +#include "user-util.h" #include "util.h" static void test_unit_name_is_valid(void) { @@ -193,15 +194,15 @@ static int test_unit_printf(void) { Unit *u, *u2; int r; - _cleanup_free_ char *mid, *bid, *host, *root_uid; - struct passwd *root; + _cleanup_free_ char *mid = NULL, *bid = NULL, *host = NULL, *uid = NULL, *user = NULL, *shell = NULL, *home = NULL; assert_se(specifier_machine_id('m', NULL, NULL, &mid) >= 0 && mid); assert_se(specifier_boot_id('b', NULL, NULL, &bid) >= 0 && bid); - assert_se((host = gethostname_malloc())); - - assert_se((root = getpwnam("root"))); - assert_se(asprintf(&root_uid, "%d", (int) root->pw_uid) > 0); + assert_se(host = gethostname_malloc()); + assert_se(user = getusername_malloc()); + assert_se(asprintf(&uid, UID_FMT, getuid())); + assert_se(get_home_dir(&home) >= 0); + assert_se(get_shell(&shell) >= 0); r = manager_new(MANAGER_USER, true, &m); if (r == -EPERM || r == -EACCES || r == -EADDRINUSE) { @@ -222,8 +223,6 @@ static int test_unit_printf(void) { assert_se(streq(t, expected)); \ } - assert_se(setenv("USER", "root", 1) == 0); - assert_se(setenv("HOME", "/root", 1) == 0); assert_se(setenv("XDG_RUNTIME_DIR", "/run/user/1/", 1) == 0); assert_se(u = unit_new(m, sizeof(Service))); @@ -242,9 +241,9 @@ static int test_unit_printf(void) { expect(u, "%p", "blah"); expect(u, "%P", "blah"); expect(u, "%i", ""); - expect(u, "%u", root->pw_name); - expect(u, "%U", root_uid); - expect(u, "%h", root->pw_dir); + expect(u, "%u", user); + expect(u, "%U", uid); + expect(u, "%h", home); expect(u, "%m", mid); expect(u, "%b", bid); expect(u, "%H", host); @@ -262,9 +261,9 @@ static int test_unit_printf(void) { expect(u2, "%P", "blah"); expect(u2, "%i", "foo-foo"); expect(u2, "%I", "foo/foo"); - expect(u2, "%u", root->pw_name); - expect(u2, "%U", root_uid); - expect(u2, "%h", root->pw_dir); + expect(u2, "%u", user); + expect(u2, "%U", uid); + expect(u2, "%h", home); expect(u2, "%m", mid); expect(u2, "%b", bid); expect(u2, "%H", host); diff --git a/src/test/test-utf8.c b/src/test/test-utf8.c index 0af8349732..e98be5763c 100644 --- a/src/test/test-utf8.c +++ b/src/test/test-utf8.c @@ -20,9 +20,9 @@ ***/ #include "alloc-util.h" +#include "string-util.h" #include "utf8.h" #include "util.h" -#include "string-util.h" static void test_utf8_is_printable(void) { assert_se(utf8_is_printable("ascii is valid\tunicode", 22)); diff --git a/src/test/test-watchdog.c b/src/test/test-watchdog.c index 2e5d0c3aae..d10d9f49af 100644 --- a/src/test/test-watchdog.c +++ b/src/test/test-watchdog.c @@ -21,8 +21,8 @@ #include <unistd.h> -#include "watchdog.h" #include "log.h" +#include "watchdog.h" int main(int argc, char *argv[]) { usec_t t = 10 * USEC_PER_SEC; diff --git a/src/timesync/timesyncd-conf.h b/src/timesync/timesyncd-conf.h index 56466fe462..cbc19c4054 100644 --- a/src/timesync/timesyncd-conf.h +++ b/src/timesync/timesyncd-conf.h @@ -22,7 +22,6 @@ ***/ #include "conf-parser.h" - #include "timesyncd-manager.h" const struct ConfigPerfItem* timesyncd_gperf_lookup(const char *key, unsigned length); diff --git a/src/timesync/timesyncd-manager.h b/src/timesync/timesyncd-manager.h index 090b2fcba8..fab22cfe84 100644 --- a/src/timesync/timesyncd-manager.h +++ b/src/timesync/timesyncd-manager.h @@ -22,8 +22,9 @@ ***/ #include "sd-event.h" -#include "sd-resolve.h" #include "sd-network.h" +#include "sd-resolve.h" + #include "list.h" #include "ratelimit.h" diff --git a/src/timesync/timesyncd-server.h b/src/timesync/timesyncd-server.h index 18c44445e1..f764d0737b 100644 --- a/src/timesync/timesyncd-server.h +++ b/src/timesync/timesyncd-server.h @@ -21,8 +21,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include "socket-util.h" #include "list.h" +#include "socket-util.h" typedef struct ServerAddress ServerAddress; typedef struct ServerName ServerName; diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c index 64f0c9396c..f9a759e223 100644 --- a/src/tmpfiles/tmpfiles.c +++ b/src/tmpfiles/tmpfiles.c @@ -1226,8 +1226,28 @@ static int create_item(Item *i) { mkdir_parents_label(i->path, 0755); if (IN_SET(i->type, CREATE_SUBVOLUME, CREATE_SUBVOLUME_INHERIT_QUOTA, CREATE_SUBVOLUME_NEW_QUOTA)) { - RUN_WITH_UMASK((~i->mode) & 0777) - r = btrfs_subvol_make(i->path); + + if (btrfs_is_subvol(isempty(arg_root) ? "/" : arg_root) <= 0) + + /* Don't create a subvolume unless the + * root directory is one, too. We do + * this under the assumption that if + * the root directory is just a plain + * directory (i.e. very light-weight), + * we shouldn't try to split it up + * into subvolumes (i.e. more + * heavy-weight). Thus, chroot() + * environments and suchlike will get + * a full brtfs subvolume set up below + * their tree only if they + * specifically set up a btrfs + * subvolume for the root dir too. */ + + r = -ENOTTY; + else { + RUN_WITH_UMASK((~i->mode) & 0777) + r = btrfs_subvol_make(i->path); + } } else r = 0; @@ -1267,6 +1287,10 @@ static int create_item(Item *i) { log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" because of read-only file system: %m", i->path); return 0; } + if (r == -ENOPROTOOPT) { + log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" because quota support is disabled: %m", i->path); + return 0; + } if (r < 0) return log_error_errno(r, "Failed to adjust quota for subvolume \"%s\": %m", i->path); if (r > 0) diff --git a/src/udev/mtd_probe/mtd_probe.c b/src/udev/mtd_probe/mtd_probe.c index 67b750c4b3..462fab7623 100644 --- a/src/udev/mtd_probe/mtd_probe.c +++ b/src/udev/mtd_probe/mtd_probe.c @@ -17,14 +17,14 @@ * Boston, MA 02110-1301 USA */ +#include <fcntl.h> +#include <mtd/mtd-user.h> #include <stdio.h> +#include <stdlib.h> #include <sys/ioctl.h> -#include <mtd/mtd-user.h> -#include <sys/types.h> #include <sys/stat.h> -#include <fcntl.h> +#include <sys/types.h> #include <unistd.h> -#include <stdlib.h> #include "mtd_probe.h" diff --git a/src/udev/mtd_probe/probe_smartmedia.c b/src/udev/mtd_probe/probe_smartmedia.c index a007ccee2f..6a6c5522a7 100644 --- a/src/udev/mtd_probe/probe_smartmedia.c +++ b/src/udev/mtd_probe/probe_smartmedia.c @@ -17,15 +17,16 @@ * Boston, MA 02110-1301 USA */ +#include <fcntl.h> +#include <mtd/mtd-user.h> +#include <stdint.h> #include <stdio.h> #include <stdlib.h> -#include <mtd/mtd-user.h> #include <string.h> -#include <sys/types.h> #include <sys/stat.h> -#include <fcntl.h> +#include <sys/types.h> #include <unistd.h> -#include <stdint.h> + #include "mtd_probe.h" static const uint8_t cis_signature[] = { diff --git a/src/udev/net/link-config.c b/src/udev/net/link-config.c index 776674e994..77d9bf995a 100644 --- a/src/udev/net/link-config.c +++ b/src/udev/net/link-config.c @@ -354,14 +354,14 @@ static int get_mac(struct udev_device *device, bool want_random, if (want_random) random_bytes(mac->ether_addr_octet, ETH_ALEN); else { - uint8_t result[8]; + uint64_t result; - r = net_get_unique_predictable_data(device, result); + r = net_get_unique_predictable_data(device, &result); if (r < 0) return r; assert_cc(ETH_ALEN <= sizeof(result)); - memcpy(mac->ether_addr_octet, result, ETH_ALEN); + memcpy(mac->ether_addr_octet, &result, ETH_ALEN); } /* see eth_random_addr in the kernel */ diff --git a/src/udev/udev-builtin-path_id.c b/src/udev/udev-builtin-path_id.c index 1e190140b2..aa18c7e420 100644 --- a/src/udev/udev-builtin-path_id.c +++ b/src/udev/udev-builtin-path_id.c @@ -593,31 +593,23 @@ static struct udev_device *handle_bcma(struct udev_device *parent, char **path) return parent; } -static struct udev_device *handle_ccw(struct udev_device *parent, struct udev_device *dev, char **path) { - struct udev_device *scsi_dev; +/* Handle devices of AP bus in System z platform. */ +static struct udev_device *handle_ap(struct udev_device *parent, char **path) { + const char *type, *func; assert(parent); - assert(dev); assert(path); - scsi_dev = udev_device_get_parent_with_subsystem_devtype(dev, "scsi", "scsi_device"); - if (scsi_dev != NULL) { - const char *wwpn; - const char *lun; - const char *hba_id; - - hba_id = udev_device_get_sysattr_value(scsi_dev, "hba_id"); - wwpn = udev_device_get_sysattr_value(scsi_dev, "wwpn"); - lun = udev_device_get_sysattr_value(scsi_dev, "fcp_lun"); - if (hba_id != NULL && lun != NULL && wwpn != NULL) { - path_prepend(path, "ccw-%s-zfcp-%s:%s", hba_id, wwpn, lun); - goto out; - } - } + type = udev_device_get_sysattr_value(parent, "type"); + func = udev_device_get_sysattr_value(parent, "ap_functions"); - path_prepend(path, "ccw-%s", udev_device_get_sysname(parent)); + if (type != NULL && func != NULL) { + path_prepend(path, "ap-%s-%s", type, func); + goto out; + } + path_prepend(path, "ap-%s", udev_device_get_sysname(parent)); out: - parent = skip_subsystem(parent, "ccw"); + parent = skip_subsystem(parent, "ap"); return parent; } @@ -629,13 +621,6 @@ static int builtin_path_id(struct udev_device *dev, int argc, char *argv[], bool assert(dev); - /* S390 ccw bus */ - parent = udev_device_get_parent_with_subsystem_devtype(dev, "ccw", NULL); - if (parent != NULL) { - handle_ccw(parent, dev, &path); - goto out; - } - /* walk up the chain of devices and compose path */ parent = dev; while (parent != NULL) { @@ -683,6 +668,25 @@ static int builtin_path_id(struct udev_device *dev, int argc, char *argv[], bool parent = skip_subsystem(parent, "scm"); supported_transport = true; supported_parent = true; + } else if (streq(subsys, "ccw")) { + path_prepend(&path, "ccw-%s", udev_device_get_sysname(parent)); + parent = skip_subsystem(parent, "ccw"); + supported_transport = true; + supported_parent = true; + } else if (streq(subsys, "ccwgroup")) { + path_prepend(&path, "ccwgroup-%s", udev_device_get_sysname(parent)); + parent = skip_subsystem(parent, "ccwgroup"); + supported_transport = true; + supported_parent = true; + } else if (streq(subsys, "ap")) { + parent = handle_ap(parent, &path); + supported_transport = true; + supported_parent = true; + } else if (streq(subsys, "iucv")) { + path_prepend(&path, "iucv-%s", udev_device_get_sysname(parent)); + parent = skip_subsystem(parent, "iucv"); + supported_transport = true; + supported_parent = true; } if (parent) @@ -705,7 +709,6 @@ static int builtin_path_id(struct udev_device *dev, int argc, char *argv[], bool if (streq(udev_device_get_subsystem(dev), "block") && !supported_transport) path = mfree(path); -out: if (path != NULL) { char tag[UTIL_NAME_SIZE]; size_t i; diff --git a/src/udev/udev-builtin-uaccess.c b/src/udev/udev-builtin-uaccess.c index bbda9de08c..3ebe36f043 100644 --- a/src/udev/udev-builtin-uaccess.c +++ b/src/udev/udev-builtin-uaccess.c @@ -18,9 +18,9 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <errno.h> #include <stdio.h> #include <stdlib.h> -#include <errno.h> #include "sd-login.h" diff --git a/src/udev/udev-ctrl.c b/src/udev/udev-ctrl.c index 1e05be51a5..962de22f43 100644 --- a/src/udev/udev-ctrl.c +++ b/src/udev/udev-ctrl.c @@ -10,13 +10,13 @@ */ #include <errno.h> -#include <stdlib.h> +#include <poll.h> #include <stddef.h> +#include <stdlib.h> #include <string.h> -#include <unistd.h> -#include <poll.h> #include <sys/socket.h> #include <sys/un.h> +#include <unistd.h> #include "alloc-util.h" #include "fd-util.h" diff --git a/src/udev/udev-watch.c b/src/udev/udev-watch.c index 9aa5ab185d..f1fdccaed8 100644 --- a/src/udev/udev-watch.c +++ b/src/udev/udev-watch.c @@ -17,12 +17,12 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include <errno.h> -#include <stdio.h> #include <dirent.h> +#include <errno.h> #include <stddef.h> -#include <unistd.h> +#include <stdio.h> #include <sys/inotify.h> +#include <unistd.h> #include "udev.h" diff --git a/src/udev/udevadm-control.c b/src/udev/udevadm-control.c index 78170463b6..989decbe95 100644 --- a/src/udev/udevadm-control.c +++ b/src/udev/udevadm-control.c @@ -13,15 +13,15 @@ */ #include <errno.h> +#include <getopt.h> +#include <stddef.h> #include <stdio.h> #include <stdlib.h> -#include <stddef.h> #include <string.h> #include <unistd.h> -#include <getopt.h> -#include "udev.h" #include "udev-util.h" +#include "udev.h" static void print_help(void) { printf("%s control COMMAND\n\n" diff --git a/src/udev/udevadm-monitor.c b/src/udev/udevadm-monitor.c index 30aa53feb2..f9cb5e63a2 100644 --- a/src/udev/udevadm-monitor.c +++ b/src/udev/udevadm-monitor.c @@ -15,15 +15,15 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include <stdio.h> -#include <stddef.h> -#include <string.h> #include <errno.h> -#include <signal.h> #include <getopt.h> -#include <time.h> -#include <sys/time.h> +#include <signal.h> +#include <stddef.h> +#include <stdio.h> +#include <string.h> #include <sys/epoll.h> +#include <sys/time.h> +#include <time.h> #include "fd-util.h" #include "formats-util.h" diff --git a/src/udev/udevadm-settle.c b/src/udev/udevadm-settle.c index c25071b0fe..6a5dc6e9e4 100644 --- a/src/udev/udevadm-settle.c +++ b/src/udev/udevadm-settle.c @@ -17,14 +17,14 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include <stdlib.h> -#include <stddef.h> -#include <string.h> -#include <stdio.h> -#include <unistd.h> #include <errno.h> #include <getopt.h> #include <poll.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> #include "parse-util.h" #include "udev.h" diff --git a/src/udev/udevd.c b/src/udev/udevd.c index 5364b92a57..6d9d765153 100644 --- a/src/udev/udevd.c +++ b/src/udev/udevd.c @@ -191,7 +191,7 @@ static void worker_free(struct worker *worker) { assert(worker->manager); - hashmap_remove(worker->manager->workers, UINT_TO_PTR(worker->pid)); + hashmap_remove(worker->manager->workers, PID_TO_PTR(worker->pid)); udev_monitor_unref(worker->monitor); event_free(worker->event); @@ -234,7 +234,7 @@ static int worker_new(struct worker **ret, Manager *manager, struct udev_monitor if (r < 0) return r; - r = hashmap_put(manager->workers, UINT_TO_PTR(pid), worker); + r = hashmap_put(manager->workers, PID_TO_PTR(pid), worker); if (r < 0) return r; @@ -891,7 +891,7 @@ static int on_worker(sd_event_source *s, int fd, uint32_t revents, void *userdat } /* lookup worker who sent the signal */ - worker = hashmap_get(manager->workers, UINT_TO_PTR(ucred->pid)); + worker = hashmap_get(manager->workers, PID_TO_PTR(ucred->pid)); if (!worker) { log_debug("worker ["PID_FMT"] returned, but is no longer tracked", ucred->pid); continue; @@ -1195,7 +1195,7 @@ static int on_sigchld(sd_event_source *s, const struct signalfd_siginfo *si, voi if (pid <= 0) break; - worker = hashmap_get(manager->workers, UINT_TO_PTR(pid)); + worker = hashmap_get(manager->workers, PID_TO_PTR(pid)); if (!worker) { log_warning("worker ["PID_FMT"] is unknown, ignoring", pid); continue; diff --git a/src/udev/v4l_id/v4l_id.c b/src/udev/v4l_id/v4l_id.c index 607d78a019..aec6676a33 100644 --- a/src/udev/v4l_id/v4l_id.c +++ b/src/udev/v4l_id/v4l_id.c @@ -13,17 +13,17 @@ * General Public License for more details: */ -#include <stdio.h> -#include <errno.h> -#include <string.h> #include <ctype.h> -#include <stdlib.h> -#include <unistd.h> +#include <errno.h> #include <fcntl.h> #include <getopt.h> -#include <sys/types.h> -#include <sys/time.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> #include <sys/ioctl.h> +#include <sys/time.h> +#include <sys/types.h> +#include <unistd.h> #include <linux/videodev2.h> #include "fd-util.h" |