diff options
Diffstat (limited to 'src')
59 files changed, 1547 insertions, 688 deletions
diff --git a/src/basic/calendarspec.c b/src/basic/calendarspec.c index 2dcc9c5575..a2296f4709 100644 --- a/src/basic/calendarspec.c +++ b/src/basic/calendarspec.c @@ -279,6 +279,9 @@ int calendar_spec_to_string(const CalendarSpec *c, char **p) { fputc(':', f); format_chain(f, 2, c->second); + if (c->utc) + fputs(" UTC", f); + r = fflush_and_check(f); if (r < 0) { free(buf); @@ -657,6 +660,10 @@ int calendar_spec_from_string(const char *p, CalendarSpec **spec) { if (!c) return -ENOMEM; + c->utc = endswith_no_case(p, "UTC"); + if (c->utc) + p = strndupa(p, strlen(p) - strlen(" UTC")); + if (strcaseeq(p, "minutely")) { r = const_chain(0, &c->second); if (r < 0) @@ -859,13 +866,13 @@ static int find_matching_component(const CalendarComponent *c, int *val) { return r; } -static bool tm_out_of_bounds(const struct tm *tm) { +static bool tm_out_of_bounds(const struct tm *tm, bool utc) { struct tm t; assert(tm); t = *tm; - if (mktime(&t) == (time_t) -1) + if (mktime_or_timegm(&t, utc) == (time_t) -1) return true; /* Did any normalization take place? If so, it was out of bounds before */ @@ -878,7 +885,7 @@ static bool tm_out_of_bounds(const struct tm *tm) { t.tm_sec != tm->tm_sec; } -static bool matches_weekday(int weekdays_bits, const struct tm *tm) { +static bool matches_weekday(int weekdays_bits, const struct tm *tm, bool utc) { struct tm t; int k; @@ -886,7 +893,7 @@ static bool matches_weekday(int weekdays_bits, const struct tm *tm) { return true; t = *tm; - if (mktime(&t) == (time_t) -1) + if (mktime_or_timegm(&t, utc) == (time_t) -1) return false; k = t.tm_wday == 0 ? 6 : t.tm_wday - 1; @@ -904,7 +911,7 @@ static int find_next(const CalendarSpec *spec, struct tm *tm) { for (;;) { /* Normalize the current date */ - mktime(&c); + mktime_or_timegm(&c, spec->utc); c.tm_isdst = -1; c.tm_year += 1900; @@ -916,7 +923,7 @@ static int find_next(const CalendarSpec *spec, struct tm *tm) { c.tm_mday = 1; c.tm_hour = c.tm_min = c.tm_sec = 0; } - if (r < 0 || tm_out_of_bounds(&c)) + if (r < 0 || tm_out_of_bounds(&c, spec->utc)) return r; c.tm_mon += 1; @@ -927,7 +934,7 @@ static int find_next(const CalendarSpec *spec, struct tm *tm) { c.tm_mday = 1; c.tm_hour = c.tm_min = c.tm_sec = 0; } - if (r < 0 || tm_out_of_bounds(&c)) { + if (r < 0 || tm_out_of_bounds(&c, spec->utc)) { c.tm_year ++; c.tm_mon = 0; c.tm_mday = 1; @@ -938,14 +945,14 @@ static int find_next(const CalendarSpec *spec, struct tm *tm) { r = find_matching_component(spec->day, &c.tm_mday); if (r > 0) c.tm_hour = c.tm_min = c.tm_sec = 0; - if (r < 0 || tm_out_of_bounds(&c)) { + 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; continue; } - if (!matches_weekday(spec->weekdays_bits, &c)) { + if (!matches_weekday(spec->weekdays_bits, &c, spec->utc)) { c.tm_mday++; c.tm_hour = c.tm_min = c.tm_sec = 0; continue; @@ -954,7 +961,7 @@ static int find_next(const CalendarSpec *spec, struct tm *tm) { r = find_matching_component(spec->hour, &c.tm_hour); if (r > 0) c.tm_min = c.tm_sec = 0; - if (r < 0 || tm_out_of_bounds(&c)) { + if (r < 0 || tm_out_of_bounds(&c, spec->utc)) { c.tm_mday ++; c.tm_hour = c.tm_min = c.tm_sec = 0; continue; @@ -963,14 +970,14 @@ static int find_next(const CalendarSpec *spec, struct tm *tm) { r = find_matching_component(spec->minute, &c.tm_min); if (r > 0) c.tm_sec = 0; - if (r < 0 || tm_out_of_bounds(&c)) { + if (r < 0 || tm_out_of_bounds(&c, spec->utc)) { c.tm_hour ++; c.tm_min = c.tm_sec = 0; continue; } r = find_matching_component(spec->second, &c.tm_sec); - if (r < 0 || tm_out_of_bounds(&c)) { + if (r < 0 || tm_out_of_bounds(&c, spec->utc)) { c.tm_min ++; c.tm_sec = 0; continue; @@ -991,13 +998,13 @@ int calendar_spec_next_usec(const CalendarSpec *spec, usec_t usec, usec_t *next) assert(next); t = (time_t) (usec / USEC_PER_SEC) + 1; - assert_se(localtime_r(&t, &tm)); + assert_se(localtime_or_gmtime_r(&t, &tm, spec->utc)); r = find_next(spec, &tm); if (r < 0) return r; - t = mktime(&tm); + t = mktime_or_timegm(&tm, spec->utc); if (t == (time_t) -1) return -EINVAL; diff --git a/src/basic/calendarspec.h b/src/basic/calendarspec.h index 7baf318249..56dc02f391 100644 --- a/src/basic/calendarspec.h +++ b/src/basic/calendarspec.h @@ -36,6 +36,7 @@ typedef struct CalendarComponent { typedef struct CalendarSpec { int weekdays_bits; + bool utc; CalendarComponent *year; CalendarComponent *month; diff --git a/src/basic/missing.h b/src/basic/missing.h index 59e835a466..5fb9951a84 100644 --- a/src/basic/missing.h +++ b/src/basic/missing.h @@ -126,6 +126,10 @@ #define SOL_NETLINK 270 #endif +#ifndef NETLINK_LIST_MEMBERSHIPS +#define NETLINK_LIST_MEMBERSHIPS 9 +#endif + #if !HAVE_DECL_PIVOT_ROOT static inline int pivot_root(const char *new_root, const char *put_old) { return syscall(SYS_pivot_root, new_root, put_old); diff --git a/src/basic/time-util.c b/src/basic/time-util.c index 531931f6e1..d4e0914b27 100644 --- a/src/basic/time-util.c +++ b/src/basic/time-util.c @@ -19,7 +19,6 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <time.h> #include <string.h> #include <sys/timex.h> #include <sys/timerfd.h> @@ -205,11 +204,8 @@ static char *format_timestamp_internal(char *buf, size_t l, usec_t t, bool utc) return NULL; sec = (time_t) (t / USEC_PER_SEC); + localtime_or_gmtime_r(&sec, &tm, utc); - if (utc) - gmtime_r(&sec, &tm); - else - localtime_r(&sec, &tm); if (strftime(buf, l, "%a %Y-%m-%d %H:%M:%S %Z", &tm) <= 0) return NULL; @@ -235,10 +231,7 @@ static char *format_timestamp_internal_us(char *buf, size_t l, usec_t t, bool ut return NULL; sec = (time_t) (t / USEC_PER_SEC); - if (utc) - gmtime_r(&sec, &tm); - else - localtime_r(&sec, &tm); + localtime_or_gmtime_r(&sec, &tm, utc); if (strftime(buf, l, "%a %Y-%m-%d %H:%M:%S", &tm) <= 0) return NULL; @@ -484,9 +477,10 @@ int parse_timestamp(const char *t, usec_t *usec) { }; const char *k; + bool utc; struct tm tm, copy; time_t x; - usec_t plus = 0, minus = 0, ret; + usec_t x_usec, plus = 0, minus = 0, ret; int r, weekday = -1; unsigned i; @@ -511,28 +505,15 @@ int parse_timestamp(const char *t, usec_t *usec) { assert(t); assert(usec); - x = time(NULL); - assert_se(localtime_r(&x, &tm)); - tm.tm_isdst = -1; - - if (streq(t, "now")) - goto finish; - - else if (streq(t, "today")) { - tm.tm_sec = tm.tm_min = tm.tm_hour = 0; - goto finish; + if (t[0] == '@') + return parse_sec(t + 1, usec); - } else if (streq(t, "yesterday")) { - tm.tm_mday --; - tm.tm_sec = tm.tm_min = tm.tm_hour = 0; - goto finish; + ret = now(CLOCK_REALTIME); - } else if (streq(t, "tomorrow")) { - tm.tm_mday ++; - tm.tm_sec = tm.tm_min = tm.tm_hour = 0; + if (streq(t, "now")) goto finish; - } else if (t[0] == '+') { + else if (t[0] == '+') { r = parse_sec(t+1, &plus); if (r < 0) return r; @@ -546,35 +527,51 @@ int parse_timestamp(const char *t, usec_t *usec) { goto finish; - } else if (t[0] == '@') - return parse_sec(t + 1, usec); - - else if (endswith(t, " ago")) { - _cleanup_free_ char *z; - - z = strndup(t, strlen(t) - 4); - if (!z) - return -ENOMEM; + } else if (endswith(t, " ago")) { + t = strndupa(t, strlen(t) - strlen(" ago")); - r = parse_sec(z, &minus); + r = parse_sec(t, &minus); if (r < 0) return r; goto finish; - } else if (endswith(t, " left")) { - _cleanup_free_ char *z; - z = strndup(t, strlen(t) - 4); - if (!z) - return -ENOMEM; + } else if (endswith(t, " left")) { + t = strndupa(t, strlen(t) - strlen(" left")); - r = parse_sec(z, &plus); + r = parse_sec(t, &plus); if (r < 0) return r; goto finish; } + utc = endswith_no_case(t, " UTC"); + if (utc) + t = strndupa(t, strlen(t) - strlen(" UTC")); + + x = ret / USEC_PER_SEC; + x_usec = 0; + + assert_se(localtime_or_gmtime_r(&x, &tm, utc)); + tm.tm_isdst = -1; + + if (streq(t, "today")) { + tm.tm_sec = tm.tm_min = tm.tm_hour = 0; + goto from_tm; + + } else if (streq(t, "yesterday")) { + tm.tm_mday --; + tm.tm_sec = tm.tm_min = tm.tm_hour = 0; + goto from_tm; + + } else if (streq(t, "tomorrow")) { + tm.tm_mday ++; + tm.tm_sec = tm.tm_min = tm.tm_hour = 0; + goto from_tm; + } + + for (i = 0; i < ELEMENTSOF(day_nr); i++) { size_t skip; @@ -592,66 +589,106 @@ int parse_timestamp(const char *t, usec_t *usec) { copy = tm; k = strptime(t, "%y-%m-%d %H:%M:%S", &tm); - if (k && *k == 0) - goto finish; + if (k) { + if (*k == '.') + goto parse_usec; + else if (*k == 0) + goto from_tm; + } tm = copy; k = strptime(t, "%Y-%m-%d %H:%M:%S", &tm); - if (k && *k == 0) - goto finish; + if (k) { + if (*k == '.') + goto parse_usec; + else if (*k == 0) + goto from_tm; + } tm = copy; k = strptime(t, "%y-%m-%d %H:%M", &tm); if (k && *k == 0) { tm.tm_sec = 0; - goto finish; + goto from_tm; } tm = copy; k = strptime(t, "%Y-%m-%d %H:%M", &tm); if (k && *k == 0) { tm.tm_sec = 0; - goto finish; + goto from_tm; } tm = copy; k = strptime(t, "%y-%m-%d", &tm); if (k && *k == 0) { tm.tm_sec = tm.tm_min = tm.tm_hour = 0; - goto finish; + goto from_tm; } tm = copy; k = strptime(t, "%Y-%m-%d", &tm); if (k && *k == 0) { tm.tm_sec = tm.tm_min = tm.tm_hour = 0; - goto finish; + goto from_tm; } tm = copy; k = strptime(t, "%H:%M:%S", &tm); - if (k && *k == 0) - goto finish; + if (k) { + if (*k == '.') + goto parse_usec; + else if (*k == 0) + goto from_tm; + } tm = copy; k = strptime(t, "%H:%M", &tm); if (k && *k == 0) { tm.tm_sec = 0; - goto finish; + goto from_tm; } return -EINVAL; -finish: - x = mktime(&tm); +parse_usec: + { + char *end; + unsigned long long val; + size_t l; + + k++; + if (*k < '0' || *k > '9') + return -EINVAL; + + /* base 10 instead of base 0, .09 is not base 8 */ + errno = 0; + val = strtoull(k, &end, 10); + if (*end || errno) + 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 = val; + } + +from_tm: + x = mktime_or_timegm(&tm, utc); if (x == (time_t) -1) return -EINVAL; if (weekday >= 0 && tm.tm_wday != weekday) return -EINVAL; - ret = (usec_t) x * USEC_PER_SEC; + ret = (usec_t) x * USEC_PER_SEC + x_usec; +finish: ret += plus; if (ret > minus) ret -= minus; @@ -1072,3 +1109,11 @@ int get_timezone(char **tz) { *tz = z; return 0; } + +time_t mktime_or_timegm(struct tm *tm, bool utc) { + return utc ? timegm(tm) : mktime(tm); +} + +struct tm *localtime_or_gmtime_r(const time_t *t, struct tm *tm, bool utc) { + return utc ? gmtime_r(t, tm) : localtime_r(t, tm); +} diff --git a/src/basic/time-util.h b/src/basic/time-util.h index 1af01541fc..417376ea96 100644 --- a/src/basic/time-util.h +++ b/src/basic/time-util.h @@ -21,8 +21,9 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <stdio.h> #include <inttypes.h> +#include <stdio.h> +#include <time.h> typedef uint64_t usec_t; typedef uint64_t nsec_t; @@ -117,3 +118,6 @@ clockid_t clock_boottime_or_monotonic(void); "xstrftime: " #buf "[] must be big enough") int get_timezone(char **timezone); + +time_t mktime_or_timegm(struct tm *tm, bool utc); +struct tm *localtime_or_gmtime_r(const time_t *t, struct tm *tm, bool utc); diff --git a/src/basic/util.c b/src/basic/util.c index ca5e4befa0..8b896a2df3 100644 --- a/src/basic/util.c +++ b/src/basic/util.c @@ -3699,6 +3699,10 @@ static const char *const log_facility_unshifted_table[LOG_NFACILITIES] = { DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(log_facility_unshifted, int, LOG_FAC(~0)); +bool log_facility_unshifted_is_valid(int facility) { + return facility >= 0 && facility <= LOG_FAC(~0); +} + static const char *const log_level_table[] = { [LOG_EMERG] = "emerg", [LOG_ALERT] = "alert", @@ -3712,6 +3716,10 @@ static const char *const log_level_table[] = { DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(log_level, int, LOG_DEBUG); +bool log_level_is_valid(int level) { + return level >= 0 && level <= LOG_DEBUG; +} + static const char* const sched_policy_table[] = { [SCHED_OTHER] = "other", [SCHED_BATCH] = "batch", diff --git a/src/basic/util.h b/src/basic/util.h index 79c7ad1b39..2544ad0830 100644 --- a/src/basic/util.h +++ b/src/basic/util.h @@ -456,9 +456,11 @@ int sigchld_code_from_string(const char *s) _pure_; int log_facility_unshifted_to_string_alloc(int i, char **s); int log_facility_unshifted_from_string(const char *s); +bool log_facility_unshifted_is_valid(int faciliy); int log_level_to_string_alloc(int i, char **s); int log_level_from_string(const char *s); +bool log_level_is_valid(int level); int sched_policy_to_string_alloc(int i, char **s); int sched_policy_from_string(const char *s); diff --git a/src/core/busname.c b/src/core/busname.c index 38becfc119..b85fce5f8d 100644 --- a/src/core/busname.c +++ b/src/core/busname.c @@ -585,6 +585,12 @@ static void busname_enter_running(BusName *n) { } if (!pending) { + if (!UNIT_ISSET(n->service)) { + log_unit_error(UNIT(n), "Service to activate vanished, refusing activation."); + r = -ENOENT; + goto fail; + } + r = manager_add_job(UNIT(n)->manager, JOB_START, UNIT_DEREF(n->service), JOB_REPLACE, true, &error, NULL); if (r < 0) goto fail; diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c index adf613d328..436229330e 100644 --- a/src/core/dbus-execute.c +++ b/src/core/dbus-execute.c @@ -83,45 +83,6 @@ static int property_get_environment_files( return sd_bus_message_close_container(reply); } -static int property_get_rlimit( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - struct rlimit *rl; - uint64_t u; - rlim_t x; - - assert(bus); - assert(reply); - assert(userdata); - - rl = *(struct rlimit**) userdata; - if (rl) - x = rl->rlim_max; - else { - struct rlimit buf = {}; - int z; - - z = rlimit_from_string(property); - assert(z >= 0); - - getrlimit(z, &buf); - x = buf.rlim_max; - } - - /* rlim_t might have different sizes, let's map - * RLIMIT_INFINITY to (uint64_t) -1, so that it is the same on - * all archs */ - u = x == RLIM_INFINITY ? (uint64_t) -1 : (uint64_t) x; - - return sd_bus_message_append(reply, "t", u); -} - static int property_get_oom_score_adjust( sd_bus *bus, const char *path, @@ -622,27 +583,63 @@ static int property_get_working_directory( return sd_bus_message_append(reply, "s", wd); } +static int property_get_syslog_level( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + ExecContext *c = userdata; + + assert(bus); + assert(reply); + assert(c); + + return sd_bus_message_append(reply, "i", LOG_PRI(c->syslog_priority)); +} + +static int property_get_syslog_facility( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + ExecContext *c = userdata; + + assert(bus); + assert(reply); + assert(c); + + return sd_bus_message_append(reply, "i", LOG_FAC(c->syslog_priority)); +} + 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("UMask", "u", bus_property_get_mode, offsetof(ExecContext, umask), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("LimitCPU", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_CPU]), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("LimitFSIZE", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_FSIZE]), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("LimitDATA", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_DATA]), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("LimitSTACK", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_STACK]), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("LimitCORE", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_CORE]), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("LimitRSS", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_RSS]), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("LimitNOFILE", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_NOFILE]), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("LimitAS", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_AS]), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("LimitNPROC", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_NPROC]), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("LimitMEMLOCK", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_MEMLOCK]), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("LimitLOCKS", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_LOCKS]), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("LimitSIGPENDING", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_SIGPENDING]), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("LimitMSGQUEUE", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_MSGQUEUE]), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("LimitNICE", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_NICE]), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("LimitRTPRIO", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_RTPRIO]), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("LimitRTTIME", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_RTTIME]), 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), + SD_BUS_PROPERTY("LimitDATA", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_DATA]), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("LimitSTACK", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_STACK]), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("LimitCORE", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_CORE]), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("LimitRSS", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_RSS]), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("LimitNOFILE", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_NOFILE]), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("LimitAS", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_AS]), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("LimitNPROC", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_NPROC]), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("LimitMEMLOCK", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_MEMLOCK]), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("LimitLOCKS", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_LOCKS]), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("LimitSIGPENDING", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_SIGPENDING]), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("LimitMSGQUEUE", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_MSGQUEUE]), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("LimitNICE", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_NICE]), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("LimitRTPRIO", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_RTPRIO]), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("LimitRTTIME", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_RTTIME]), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("WorkingDirectory", "s", property_get_working_directory, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("RootDirectory", "s", NULL, offsetof(ExecContext, root_directory), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("OOMScoreAdjust", "i", property_get_oom_score_adjust, 0, SD_BUS_VTABLE_PROPERTY_CONST), @@ -664,6 +661,8 @@ const sd_bus_vtable bus_exec_vtable[] = { SD_BUS_PROPERTY("SyslogPriority", "i", bus_property_get_int, offsetof(ExecContext, syslog_priority), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("SyslogIdentifier", "s", NULL, offsetof(ExecContext, syslog_identifier), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("SyslogLevelPrefix", "b", bus_property_get_bool, offsetof(ExecContext, syslog_level_prefix), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("SyslogLevel", "i", property_get_syslog_level, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("SyslogFacility", "i", property_get_syslog_facility, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("Capabilities", "s", property_get_capabilities, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("SecureBits", "i", bus_property_get_int, offsetof(ExecContext, secure_bits), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("CapabilityBoundingSet", "t", property_get_capability_bounding_set, 0, SD_BUS_VTABLE_PROPERTY_CONST), @@ -856,7 +855,64 @@ int bus_exec_context_set_transient_property( } return 1; + } else if (streq(name, "SyslogIdentifier")) { + const char *id; + + r = sd_bus_message_read(message, "s", &id); + if (r < 0) + return r; + + if (mode != UNIT_CHECK) { + + if (isempty(id)) { + c->syslog_identifier = mfree(c->syslog_identifier); + } else { + char *t; + + t = strdup(id); + if (!t) + return -ENOMEM; + + free(c->syslog_identifier); + c->syslog_identifier = t; + } + + unit_write_drop_in_private_format(u, mode, name, "SyslogIdentifier=%s\n", id); + } + + return 1; + } else if (streq(name, "SyslogLevel")) { + int level; + + r = sd_bus_message_read(message, "i", &level); + if (r < 0) + return r; + + if (!log_level_is_valid(level)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Log level value out of range"); + + if (mode != UNIT_CHECK) { + c->syslog_priority = (c->syslog_priority & LOG_FACMASK) | level; + unit_write_drop_in_private_format(u, mode, name, "SyslogLevel=%i\n", level); + } + + return 1; + } else if (streq(name, "SyslogFacility")) { + int facility; + r = sd_bus_message_read(message, "i", &facility); + if (r < 0) + return r; + + if (!log_facility_unshifted_is_valid(facility)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Log facility value out of range"); + + if (mode != UNIT_CHECK) { + c->syslog_priority = (facility << 3) | LOG_PRI(c->syslog_priority); + unit_write_drop_in_private_format(u, mode, name, "SyslogFacility=%i\n", facility); + } + + return 1; } else if (streq(name, "Nice")) { int n; @@ -998,7 +1054,7 @@ int bus_exec_context_set_transient_property( } else if (STR_IN_SET(name, "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "PrivateTmp", "PrivateDevices", "PrivateNetwork", - "NoNewPrivileges")) { + "NoNewPrivileges", "SyslogLevelPrefix")) { int b; r = sd_bus_message_read(message, "b", &b); @@ -1020,6 +1076,8 @@ int bus_exec_context_set_transient_property( c->private_network = b; else if (streq(name, "NoNewPrivileges")) c->no_new_privileges = b; + else if (streq(name, "SyslogLevelPrefix")) + c->syslog_level_prefix = b; unit_write_drop_in_private_format(u, mode, name, "%s=%s\n", name, yes_no(b)); } @@ -1113,6 +1171,21 @@ int bus_exec_context_set_transient_property( return 1; + } else if (streq(name, "TimerSlackNSec")) { + + nsec_t n; + + r = sd_bus_message_read(message, "t", &n); + if (r < 0) + return r; + + if (mode != UNIT_CHECK) { + c->timer_slack_nsec = n; + unit_write_drop_in_private_format(u, mode, name, "TimerSlackNSec=" NSEC_FMT "\n", n); + } + + return 1; + } else if (rlimit_from_string(name) >= 0) { uint64_t rl; rlim_t x; diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c index 9e21b0787d..00bb391106 100644 --- a/src/core/dbus-manager.c +++ b/src/core/dbus-manager.c @@ -20,6 +20,7 @@ ***/ #include <errno.h> +#include <sys/prctl.h> #include <unistd.h> #include "log.h" @@ -122,8 +123,7 @@ static int property_get_tainted( void *userdata, sd_bus_error *error) { - char buf[sizeof("split-usr:mtab-not-symlink:cgroups-missing:local-hwclock:")] = "", *e = buf; - _cleanup_free_ char *p = NULL; + char buf[sizeof("split-usr:cgroups-missing:local-hwclock:")] = "", *e = buf; Manager *m = userdata; assert(bus); @@ -133,9 +133,6 @@ static int property_get_tainted( if (m->taint_usr) e = stpcpy(e, "split-usr:"); - if (readlink_malloc("/etc/mtab", &p) < 0) - e = stpcpy(e, "mtab-not-symlink:"); - if (access("/proc/cgroups", F_OK) < 0) e = stpcpy(e, "cgroups-missing:"); @@ -350,6 +347,21 @@ static int property_set_runtime_watchdog( return watchdog_set_timeout(t); } +static int property_get_timer_slack_nsec( + 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); + + return sd_bus_message_append(reply, "t", (uint64_t) prctl(PR_GET_TIMERSLACK)); +} + static int method_get_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) { _cleanup_free_ char *path = NULL; Manager *m = userdata; @@ -1981,6 +1993,23 @@ const sd_bus_vtable bus_manager_vtable[] = { SD_BUS_PROPERTY("DefaultBlockIOAccounting", "b", bus_property_get_bool, offsetof(Manager, default_blockio_accounting), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("DefaultMemoryAccounting", "b", bus_property_get_bool, offsetof(Manager, default_memory_accounting), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("DefaultTasksAccounting", "b", bus_property_get_bool, offsetof(Manager, default_tasks_accounting), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("DefaultLimitCPU", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_CPU]), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("DefaultLimitFSIZE", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_FSIZE]), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("DefaultLimitDATA", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_DATA]), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("DefaultLimitSTACK", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_STACK]), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("DefaultLimitCORE", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_CORE]), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("DefaultLimitRSS", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_RSS]), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("DefaultLimitNOFILE", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_NOFILE]), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("DefaultLimitAS", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_AS]), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("DefaultLimitNPROC", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_NPROC]), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("DefaultLimitMEMLOCK", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_MEMLOCK]), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("DefaultLimitLOCKS", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_LOCKS]), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("DefaultLimitSIGPENDING", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_SIGPENDING]), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("DefaultLimitMSGQUEUE", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_MSGQUEUE]), SD_BUS_VTABLE_PROPERTY_CONST), + 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("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), SD_BUS_METHOD("GetUnitByPID", "u", "o", method_get_unit_by_pid, SD_BUS_VTABLE_UNPRIVILEGED), diff --git a/src/core/execute.c b/src/core/execute.c index d6217840c0..51efb7c215 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -2314,7 +2314,7 @@ static void strv_fprintf(FILE *f, char **l) { } void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) { - char **e; + char **e, **d; unsigned i; assert(c); @@ -2350,6 +2350,11 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) { STRV_FOREACH(e, c->environment_files) fprintf(f, "%sEnvironmentFile: %s\n", prefix, *e); + fprintf(f, "%sRuntimeDirectoryMode: %04o\n", prefix, c->runtime_directory_mode); + + STRV_FOREACH(d, c->runtime_directory) + fprintf(f, "%sRuntimeDirectory: %s\n", prefix, *d); + if (c->nice_set) fprintf(f, "%sNice: %i\n", diff --git a/src/core/manager.c b/src/core/manager.c index 526d4d1cef..b2d56e88a7 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -3013,30 +3013,6 @@ void manager_status_printf(Manager *m, StatusType type, const char *status, cons va_end(ap); } -int manager_get_unit_by_path(Manager *m, const char *path, const char *suffix, Unit **_found) { - _cleanup_free_ char *p = NULL; - Unit *found; - int r; - - assert(m); - assert(path); - assert(suffix); - assert(_found); - - r = unit_name_from_path(path, suffix, &p); - if (r < 0) - return r; - - found = manager_get_unit(m, p); - if (!found) { - *_found = NULL; - return 0; - } - - *_found = found; - return 1; -} - Set *manager_get_units_requiring_mounts_for(Manager *m, const char *path) { char p[strlen(path)+1]; diff --git a/src/core/manager.h b/src/core/manager.h index fad10aaacf..38d2770e97 100644 --- a/src/core/manager.h +++ b/src/core/manager.h @@ -322,8 +322,6 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds); Job *manager_get_job(Manager *m, uint32_t id); Unit *manager_get_unit(Manager *m, const char *name); -int manager_get_unit_by_path(Manager *m, const char *path, const char *suffix, Unit **_found); - int manager_get_job_from_dbus_path(Manager *m, const char *s, Job **_j); int manager_load_unit_prepare(Manager *m, const char *name, const char *path, sd_bus_error *e, Unit **_ret); diff --git a/src/core/scope.c b/src/core/scope.c index 7325e3601b..ea7d846578 100644 --- a/src/core/scope.c +++ b/src/core/scope.c @@ -534,6 +534,9 @@ static int scope_enumerate(Manager *m) { u->transient = true; u->default_dependencies = false; u->no_gc = true; + u->ignore_on_isolate = true; + u->refuse_manual_start = true; + u->refuse_manual_stop = true; SCOPE(u)->deserialized_state = SCOPE_RUNNING; SCOPE(u)->kill_context.kill_signal = SIGRTMIN+14; diff --git a/src/core/slice.c b/src/core/slice.c index 063024134a..1542e83eb6 100644 --- a/src/core/slice.c +++ b/src/core/slice.c @@ -274,6 +274,9 @@ static int slice_enumerate(Manager *m) { u->default_dependencies = false; u->no_gc = true; + u->ignore_on_isolate = true; + u->refuse_manual_start = true; + u->refuse_manual_stop = true; SLICE(u)->deserialized_state = SLICE_ACTIVE; if (!u->description) diff --git a/src/core/unit.c b/src/core/unit.c index 423caa9562..39cd89f1e3 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -1147,13 +1147,23 @@ static int unit_add_mount_dependencies(Unit *u) { char prefix[strlen(*i) + 1]; PATH_FOREACH_PREFIX_MORE(prefix, *i) { + _cleanup_free_ char *p = NULL; Unit *m; - r = manager_get_unit_by_path(u->manager, prefix, ".mount", &m); + r = unit_name_from_path(prefix, ".mount", &p); if (r < 0) return r; - if (r == 0) + + m = manager_get_unit(u->manager, p); + if (!m) { + /* Make sure to load the mount unit if + * it exists. If so the dependencies + * on this unit will be added later + * during the loading of the mount + * unit. */ + (void) manager_load_unit_prepare(u->manager, p, NULL, NULL, &m); continue; + } if (m == u) continue; diff --git a/src/journal/compress.c b/src/journal/compress.c index c66043e503..8c92e26edd 100644 --- a/src/journal/compress.c +++ b/src/journal/compress.c @@ -22,6 +22,7 @@ #include <stdlib.h> #include <string.h> #include <unistd.h> +#include <sys/mman.h> #ifdef HAVE_XZ # include <lzma.h> @@ -29,6 +30,7 @@ #ifdef HAVE_LZ4 # include <lz4.h> +# include <lz4frame.h> #endif #include "compress.h" @@ -37,6 +39,11 @@ #include "sparse-endian.h" #include "journal-def.h" +#ifdef HAVE_LZ4 +DEFINE_TRIVIAL_CLEANUP_FUNC(LZ4F_compressionContext_t, LZ4F_freeCompressionContext); +DEFINE_TRIVIAL_CLEANUP_FUNC(LZ4F_decompressionContext_t, LZ4F_freeDecompressionContext); +#endif + #define ALIGN_8(l) ALIGN_TO(l, sizeof(size_t)) static const char* const object_compressed_table[_OBJECT_COMPRESSED_MAX] = { @@ -416,81 +423,96 @@ int compress_stream_xz(int fdf, int fdt, uint64_t max_bytes) { #endif } -#define LZ4_BUFSIZE (512*1024) +#define LZ4_BUFSIZE (512*1024u) int compress_stream_lz4(int fdf, int fdt, uint64_t max_bytes) { #ifdef HAVE_LZ4 + LZ4F_errorCode_t c; + _cleanup_(LZ4F_freeCompressionContextp) LZ4F_compressionContext_t ctx = NULL; + _cleanup_free_ char *buf = NULL; + char *src = NULL; + size_t size, n, total_in = 0, total_out = 0, offset = 0, frame_size; + struct stat st; + int r; + static const LZ4F_compressOptions_t options = { + .stableSrc = 1, + }; + static const LZ4F_preferences_t preferences = { + .frameInfo.blockSizeID = 5, + }; - _cleanup_free_ char *buf1 = NULL, *buf2 = NULL, *out = NULL; - char *buf; - LZ4_stream_t lz4_data = {}; - le32_t header; - size_t total_in = 0, total_out = sizeof(header); - ssize_t n; + c = LZ4F_createCompressionContext(&ctx, LZ4F_VERSION); + if (LZ4F_isError(c)) + return -ENOMEM; - assert(fdf >= 0); - assert(fdt >= 0); + if (fstat(fdf, &st) < 0) + return log_debug_errno(errno, "fstat() failed: %m"); - buf1 = malloc(LZ4_BUFSIZE); - buf2 = malloc(LZ4_BUFSIZE); - out = malloc(LZ4_COMPRESSBOUND(LZ4_BUFSIZE)); - if (!buf1 || !buf2 || !out) - return log_oom(); + frame_size = LZ4F_compressBound(LZ4_BUFSIZE, &preferences); + size = frame_size + 64*1024; /* add some space for header and trailer */ + buf = malloc(size); + if (!buf) + return -ENOMEM; - buf = buf1; - for (;;) { - size_t m; - int r; + n = offset = LZ4F_compressBegin(ctx, buf, size, &preferences); + if (LZ4F_isError(n)) + return -EINVAL; - m = LZ4_BUFSIZE; - if (max_bytes != (uint64_t) -1 && (uint64_t) m > (max_bytes - total_in)) - m = (size_t) (max_bytes - total_in); + src = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fdf, 0); + if (src == MAP_FAILED) + return -errno; - n = read(fdf, buf, m); - if (n < 0) - return -errno; - if (n == 0) - break; + log_debug("Buffer size is %zu bytes, header size %zu bytes.", size, n); - total_in += n; + while (total_in < (size_t) st.st_size) { + ssize_t k; - r = LZ4_compress_continue(&lz4_data, buf, out, n); - if (r == 0) { - log_error("LZ4 compression failed."); - return -EBADMSG; + k = MIN(LZ4_BUFSIZE, st.st_size - total_in); + n = LZ4F_compressUpdate(ctx, buf + offset, size - offset, + src + total_in, k, &options); + if (LZ4F_isError(n)) { + r = -ENOTRECOVERABLE; + goto cleanup; } - header = htole32(r); - errno = 0; + total_in += k; + offset += n; + total_out += n; - n = write(fdt, &header, sizeof(header)); - if (n < 0) - return -errno; - if (n != sizeof(header)) - return errno ? -errno : -EIO; - - n = loop_write(fdt, out, r, false); - if (n < 0) - return n; + if (max_bytes != (uint64_t) -1 && total_out > (size_t) max_bytes) { + log_debug("Compressed stream longer than %zd bytes", max_bytes); + return -EFBIG; + } - total_out += sizeof(header) + r; + if (size - offset < frame_size + 4) { + k = loop_write(fdt, buf, offset, false); + if (k < 0) { + r = k; + goto cleanup; + } + offset = 0; + } + } - buf = buf == buf1 ? buf2 : buf1; + n = LZ4F_compressEnd(ctx, buf + offset, size - offset, &options); + if (LZ4F_isError(n)) { + r = -ENOTRECOVERABLE; + goto cleanup; } - header = htole32(0); - n = write(fdt, &header, sizeof(header)); - if (n < 0) - return -errno; - if (n != sizeof(header)) - return errno ? -errno : -EIO; + offset += n; + total_out += n; + r = loop_write(fdt, buf, offset, false); + if (r < 0) + goto cleanup; log_debug("LZ4 compression finished (%zu -> %zu bytes, %.1f%%)", total_in, total_out, (double) total_out / total_in * 100); - - return 0; + cleanup: + munmap(src, st.st_size); + return r; #else return -EPROTONOSUPPORT; #endif @@ -510,7 +532,7 @@ int decompress_stream_xz(int fdf, int fdt, uint64_t max_bytes) { ret = lzma_stream_decoder(&s, UINT64_MAX, 0); if (ret != LZMA_OK) { - log_error("Failed to initialize XZ decoder: code %u", ret); + log_debug("Failed to initialize XZ decoder: code %u", ret); return -ENOMEM; } @@ -536,7 +558,7 @@ int decompress_stream_xz(int fdf, int fdt, uint64_t max_bytes) { ret = lzma_code(&s, action); if (ret != LZMA_OK && ret != LZMA_STREAM_END) { - log_error("Decompression failed: code %u", ret); + log_debug("Decompression failed: code %u", ret); return -EBADMSG; } @@ -566,14 +588,14 @@ int decompress_stream_xz(int fdf, int fdt, uint64_t max_bytes) { } } #else - log_error("Cannot decompress file. Compiled without XZ support."); + log_debug("Cannot decompress file. Compiled without XZ support."); return -EPROTONOSUPPORT; #endif } -int decompress_stream_lz4(int fdf, int fdt, uint64_t max_bytes) { - #ifdef HAVE_LZ4 +static int decompress_stream_lz4_v1(int fdf, int fdt, uint64_t max_bytes) { + _cleanup_free_ char *buf = NULL, *out = NULL; size_t buf_size = 0; LZ4_streamDecode_t lz4_data = {}; @@ -585,7 +607,7 @@ int decompress_stream_lz4(int fdf, int fdt, uint64_t max_bytes) { out = malloc(4*LZ4_BUFSIZE); if (!out) - return log_oom(); + return -ENOMEM; for (;;) { ssize_t m; @@ -606,22 +628,24 @@ int decompress_stream_lz4(int fdf, int fdt, uint64_t max_bytes) { * not accept buffers compressed by newer binaries then. */ if (m > LZ4_COMPRESSBOUND(LZ4_BUFSIZE * 4)) { - log_error("Compressed stream block too big: %zd bytes", m); - return -EBADMSG; + log_debug("Compressed stream block too big: %zd bytes", m); + return -ENOBUFS; } total_in += sizeof(header) + m; if (!GREEDY_REALLOC(buf, buf_size, m)) - return log_oom(); + return -ENOMEM; r = loop_read_exact(fdf, buf, m, false); if (r < 0) return r; r = LZ4_decompress_safe_continue(&lz4_data, buf, out, m, 4*LZ4_BUFSIZE); - if (r <= 0) - log_error("LZ4 decompression failed."); + if (r <= 0) { + log_debug("LZ4 decompression failed (legacy format)."); + return -EBADMSG; + } total_out += r; @@ -635,13 +659,80 @@ int decompress_stream_lz4(int fdf, int fdt, uint64_t max_bytes) { return r; } - log_debug("LZ4 decompression finished (%zu -> %zu bytes, %.1f%%)", + log_debug("LZ4 decompression finished (legacy format, %zu -> %zu bytes, %.1f%%)", total_in, total_out, (double) total_out / total_in * 100); return 0; +} + +static int decompress_stream_lz4_v2(int in, int out, uint64_t max_bytes) { + size_t c; + _cleanup_(LZ4F_freeDecompressionContextp) LZ4F_decompressionContext_t ctx = NULL; + _cleanup_free_ char *buf = NULL; + char *src; + struct stat st; + int r = 0; + size_t total_in = 0, total_out = 0; + + c = LZ4F_createDecompressionContext(&ctx, LZ4F_VERSION); + if (LZ4F_isError(c)) + return -ENOMEM; + + if (fstat(in, &st) < 0) + return log_debug_errno(errno, "fstat() failed: %m"); + + buf = malloc(LZ4_BUFSIZE); + if (!buf) + return -ENOMEM; + + src = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, in, 0); + if (src == MAP_FAILED) + return -errno; + + while (total_in < (size_t) st.st_size) { + size_t produced = LZ4_BUFSIZE; + size_t used = st.st_size - total_in; + + c = LZ4F_decompress(ctx, buf, &produced, src + total_in, &used, NULL); + if (LZ4F_isError(c)) { + r = -EBADMSG; + goto cleanup; + } + + total_in += used; + total_out += produced; + + if (max_bytes != (uint64_t) -1 && total_out > (size_t) max_bytes) { + log_debug("Decompressed stream longer than %zd bytes", max_bytes); + r = -EFBIG; + goto cleanup; + } + + r = loop_write(out, buf, produced, false); + if (r < 0) + goto cleanup; + } + + log_debug("LZ4 decompression finished (%zu -> %zu bytes, %.1f%%)", + total_in, total_out, + (double) total_out / total_in * 100); + cleanup: + munmap(src, st.st_size); + return r; +} +#endif + +int decompress_stream_lz4(int fdf, int fdt, uint64_t max_bytes) { +#ifdef HAVE_LZ4 + int r; + + r = decompress_stream_lz4_v2(fdf, fdt, max_bytes); + if (r == -EBADMSG) + r = decompress_stream_lz4_v1(fdf, fdt, max_bytes); + return r; #else - log_error("Cannot decompress file. Compiled without LZ4 support."); + log_debug("Cannot decompress file. Compiled without LZ4 support."); return -EPROTONOSUPPORT; #endif } diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c index d9851db36c..5dafb0bcab 100644 --- a/src/journal/journalctl.c +++ b/src/journal/journalctl.c @@ -194,8 +194,8 @@ static void help(void) { " --system Show the system journal\n" " --user Show the user journal for the current user\n" " -M --machine=CONTAINER Operate on local container\n" - " --since=DATE Show entries not older than the specified date\n" - " --until=DATE Show entries not newer than the specified date\n" + " -S --since=DATE Show entries not older than the specified date\n" + " -U --until=DATE Show entries not newer than the specified date\n" " -c --cursor=CURSOR Show entries starting at the specified cursor\n" " --after-cursor=CURSOR Show entries after the specified cursor\n" " --show-cursor Print the cursor after all the entries\n" @@ -270,8 +270,6 @@ static int parse_argv(int argc, char *argv[]) { ARG_VERIFY, ARG_VERIFY_KEY, ARG_DISK_USAGE, - ARG_SINCE, - ARG_UNTIL, ARG_AFTER_CURSOR, ARG_SHOW_CURSOR, ARG_USER_UNIT, @@ -323,8 +321,8 @@ static int parse_argv(int argc, char *argv[]) { { "cursor", required_argument, NULL, 'c' }, { "after-cursor", required_argument, NULL, ARG_AFTER_CURSOR }, { "show-cursor", no_argument, NULL, ARG_SHOW_CURSOR }, - { "since", required_argument, NULL, ARG_SINCE }, - { "until", required_argument, NULL, ARG_UNTIL }, + { "since", required_argument, NULL, 'S' }, + { "until", required_argument, NULL, 'U' }, { "unit", required_argument, NULL, 'u' }, { "user-unit", required_argument, NULL, ARG_USER_UNIT }, { "field", required_argument, NULL, 'F' }, @@ -348,7 +346,7 @@ static int parse_argv(int argc, char *argv[]) { assert(argc >= 0); assert(argv); - while ((c = getopt_long(argc, argv, "hefo:aln::qmb::kD:p:c:t:u:F:xrM:", options, NULL)) >= 0) + while ((c = getopt_long(argc, argv, "hefo:aln::qmb::kD:p:c:S:U:t:u:F:xrM:", options, NULL)) >= 0) switch (c) { @@ -646,7 +644,7 @@ static int parse_argv(int argc, char *argv[]) { break; } - case ARG_SINCE: + case 'S': r = parse_timestamp(optarg, &arg_since); if (r < 0) { log_error("Failed to parse timestamp: %s", optarg); @@ -655,7 +653,7 @@ static int parse_argv(int argc, char *argv[]) { arg_since_set = true; break; - case ARG_UNTIL: + case 'U': r = parse_timestamp(optarg, &arg_until); if (r < 0) { log_error("Failed to parse timestamp: %s", optarg); @@ -748,7 +746,7 @@ static int parse_argv(int argc, char *argv[]) { return -EINVAL; } - if (arg_action != ACTION_SHOW && optind < argc) { + if (!IN_SET(arg_action, ACTION_SHOW, ACTION_DUMP_CATALOG, ACTION_LIST_CATALOG) && optind < argc) { log_error("Extraneous arguments starting with '%s'", argv[optind]); return -EINVAL; } @@ -1879,6 +1877,7 @@ int main(int argc, char *argv[]) { } else { bool oneline = arg_action == ACTION_LIST_CATALOG; + pager_open_if_enabled(); if (optind < argc) r = catalog_list_items(stdout, database, oneline, argv + optind); diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c index fb172b7f5d..2d2a215f5d 100644 --- a/src/journal/journald-server.c +++ b/src/journal/journald-server.c @@ -1446,6 +1446,7 @@ static int server_open_hostname(Server *s) { int server_init(Server *s) { _cleanup_fdset_free_ FDSet *fds = NULL; int n, r, fd; + bool no_sockets; assert(s); @@ -1555,30 +1556,44 @@ int server_init(Server *s) { } } - r = server_open_stdout_socket(s, fds); - if (r < 0) - return r; + /* Try to restore streams, but don't bother if this fails */ + (void) server_restore_streams(s, fds); if (fdset_size(fds) > 0) { log_warning("%u unknown file descriptors passed, closing.", fdset_size(fds)); fds = fdset_free(fds); } + no_sockets = s->native_fd < 0 && s->stdout_fd < 0 && s->syslog_fd < 0 && s->audit_fd < 0; + + /* always open stdout, syslog, native, and kmsg sockets */ + + /* systemd-journald.socket: /run/systemd/journal/stdout */ + r = server_open_stdout_socket(s); + if (r < 0) + return r; + + /* systemd-journald-dev-log.socket: /run/systemd/journal/dev-log */ r = server_open_syslog_socket(s); if (r < 0) return r; + /* systemd-journald.socket: /run/systemd/journal/socket */ r = server_open_native_socket(s); if (r < 0) return r; + /* /dev/ksmg */ r = server_open_dev_kmsg(s); if (r < 0) return r; - r = server_open_audit(s); - if (r < 0) - return r; + /* Unless we got *some* sockets and not audit, open audit socket */ + if (s->audit_fd >= 0 || no_sockets) { + r = server_open_audit(s); + if (r < 0) + return r; + } r = server_open_kernel_seqnum(s); if (r < 0) diff --git a/src/journal/journald-stream.c b/src/journal/journald-stream.c index 69e2d41863..cbdaa3b888 100644 --- a/src/journal/journald-stream.c +++ b/src/journal/journald-stream.c @@ -627,7 +627,7 @@ static int stdout_stream_restore(Server *s, const char *fname, int fd) { return 0; } -static int server_restore_streams(Server *s, FDSet *fds) { +int server_restore_streams(Server *s, FDSet *fds) { _cleanup_closedir_ DIR *d = NULL; struct dirent *de; int r; @@ -681,7 +681,7 @@ fail: return log_error_errno(errno, "Failed to read streams directory: %m"); } -int server_open_stdout_socket(Server *s, FDSet *fds) { +int server_open_stdout_socket(Server *s) { int r; assert(s); @@ -717,8 +717,5 @@ int server_open_stdout_socket(Server *s, FDSet *fds) { if (r < 0) return log_error_errno(r, "Failed to adjust priority of stdout server event source: %m"); - /* Try to restore streams, but don't bother if this fails */ - (void) server_restore_streams(s, fds); - return 0; } diff --git a/src/journal/journald-stream.h b/src/journal/journald-stream.h index 94bf955d78..257dce45df 100644 --- a/src/journal/journald-stream.h +++ b/src/journal/journald-stream.h @@ -24,6 +24,6 @@ #include "fdset.h" #include "journald-server.h" -int server_open_stdout_socket(Server *s, FDSet *fds); - +int server_open_stdout_socket(Server *s); +int server_restore_streams(Server *s, FDSet *fds); void stdout_stream_free(StdoutStream *s); diff --git a/src/journal/test-compress-benchmark.c b/src/journal/test-compress-benchmark.c index c8e5b76c6c..0be6820a14 100644 --- a/src/journal/test-compress-benchmark.c +++ b/src/journal/test-compress-benchmark.c @@ -20,6 +20,7 @@ #include "compress.h" #include "util.h" #include "macro.h" +#include "random-util.h" typedef int (compress_t)(const void *src, uint64_t src_size, void *dst, size_t *dst_size); typedef int (decompress_t)(const void *src, uint64_t src_size, @@ -27,20 +28,31 @@ typedef int (decompress_t)(const void *src, uint64_t src_size, #define MAX_SIZE (1024*1024LU) -static char* make_buf(size_t count) { +static char* make_buf(size_t count, const char *type) { char *buf; size_t i; buf = malloc(count); assert_se(buf); - for (i = 0; i < count; i++) - buf[i] = 'a' + i % ('z' - 'a' + 1); + if (streq(type, "zeros")) + memzero(buf, count); + else if (streq(type, "simple")) + for (i = 0; i < count; i++) + buf[i] = 'a' + i % ('z' - 'a' + 1); + else if (streq(type, "random")) { + random_bytes(buf, count/10); + random_bytes(buf + 2*count/10, count/10); + random_bytes(buf + 4*count/10, count/20); + random_bytes(buf + 6*count/10, count/20); + random_bytes(buf + 8*count/10, count/20); + } else + assert_not_reached("here"); return buf; } -static void test_compress_decompress(const char* label, +static void test_compress_decompress(const char* label, const char* type, compress_t compress, decompress_t decompress) { usec_t n, n2 = 0; float dt; @@ -50,7 +62,7 @@ static void test_compress_decompress(const char* label, size_t buf2_allocated = 0; size_t skipped = 0, compressed = 0, total = 0; - text = make_buf(MAX_SIZE); + text = make_buf(MAX_SIZE, type); buf = calloc(MAX_SIZE + 1, 1); assert_se(text && buf); @@ -62,7 +74,8 @@ static void test_compress_decompress(const char* label, r = compress(text, i, buf, &j); /* assume compression must be successful except for small inputs */ - assert_se(r == 0 || (i < 2048 && r == -ENOBUFS)); + assert_se(r == 0 || (i < 2048 && r == -ENOBUFS) || streq(type, "random")); + /* check for overwrites */ assert_se(buf[i] == 0); if (r != 0) { @@ -91,23 +104,26 @@ static void test_compress_decompress(const char* label, dt = (n2-n) / 1e6; - log_info("%s: compressed & decompressed %zu bytes in %.2fs (%.2fMiB/s), " + log_info("%s/%s: compressed & decompressed %zu bytes in %.2fs (%.2fMiB/s), " "mean compresion %.2f%%, skipped %zu bytes", - label, total, dt, + label, type, total, dt, total / 1024. / 1024 / dt, 100 - compressed * 100. / total, skipped); } int main(int argc, char *argv[]) { + const char *i; log_set_max_level(LOG_DEBUG); + NULSTR_FOREACH(i, "zeros\0simple\0random\0") { #ifdef HAVE_XZ - test_compress_decompress("XZ", compress_blob_xz, decompress_blob_xz); + test_compress_decompress("XZ", i, compress_blob_xz, decompress_blob_xz); #endif #ifdef HAVE_LZ4 - test_compress_decompress("LZ4", compress_blob_lz4, decompress_blob_lz4); + test_compress_decompress("LZ4", i, compress_blob_lz4, decompress_blob_lz4); #endif + } return 0; } diff --git a/src/journal/test-compress.c b/src/journal/test-compress.c index f17c00e60d..e51b12407f 100644 --- a/src/journal/test-compress.c +++ b/src/journal/test-compress.c @@ -144,8 +144,8 @@ static void test_compress_stream(int compression, const char *srcfile) { _cleanup_close_ int src = -1, dst = -1, dst2 = -1; - char pattern[] = "/tmp/systemd-test.xz.XXXXXX", - pattern2[] = "/tmp/systemd-test.xz.XXXXXX"; + char pattern[] = "/tmp/systemd-test.compressed.XXXXXX", + pattern2[] = "/tmp/systemd-test.compressed.XXXXXX"; int r; _cleanup_free_ char *cmd = NULL, *cmd2; struct stat st = {}; @@ -185,7 +185,7 @@ static void test_compress_stream(int compression, assert_se(lseek(dst, 1, SEEK_SET) == 1); r = decompress(dst, dst2, st.st_size); - assert_se(r == -EBADMSG); + assert_se(r == -EBADMSG || r == 0); assert_se(lseek(dst, 0, SEEK_SET) == 0); assert_se(lseek(dst2, 0, SEEK_SET) == 0); @@ -236,8 +236,7 @@ int main(int argc, char *argv[]) { compress_blob_lz4, decompress_startswith_lz4, data, sizeof(data), true); - /* Produced stream is not compatible with lz4 binary, skip lz4cat check. */ - test_compress_stream(OBJECT_COMPRESSED_LZ4, NULL, + test_compress_stream(OBJECT_COMPRESSED_LZ4, "lz4cat", compress_stream_lz4, decompress_stream_lz4, argv[0]); #else log_info("/* LZ4 test skipped */"); diff --git a/src/libsystemd-network/sd-dhcp-client.c b/src/libsystemd-network/sd-dhcp-client.c index 141b836a0d..28e012afca 100644 --- a/src/libsystemd-network/sd-dhcp-client.c +++ b/src/libsystemd-network/sd-dhcp-client.c @@ -1265,8 +1265,7 @@ static int client_set_lease_timeouts(sd_dhcp_client *client) { return r; log_dhcp_client(client, "lease expires in %s", - format_timespan(time_string, FORMAT_TIMESPAN_MAX, - lifetime_timeout - time_now, 0)); + format_timespan(time_string, FORMAT_TIMESPAN_MAX, lifetime_timeout - time_now, USEC_PER_SEC)); /* don't arm earlier timeouts if this has already expired */ if (lifetime_timeout <= time_now) @@ -1292,8 +1291,7 @@ static int client_set_lease_timeouts(sd_dhcp_client *client) { return r; log_dhcp_client(client, "T2 expires in %s", - format_timespan(time_string, FORMAT_TIMESPAN_MAX, - t2_timeout - time_now, 0)); + format_timespan(time_string, FORMAT_TIMESPAN_MAX, t2_timeout - time_now, USEC_PER_SEC)); /* don't arm earlier timeout if this has already expired */ if (t2_timeout <= time_now) @@ -1318,8 +1316,7 @@ static int client_set_lease_timeouts(sd_dhcp_client *client) { return r; log_dhcp_client(client, "T1 expires in %s", - format_timespan(time_string, FORMAT_TIMESPAN_MAX, - t1_timeout - time_now, 0)); + format_timespan(time_string, FORMAT_TIMESPAN_MAX, t1_timeout - time_now, USEC_PER_SEC)); return 0; } diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index acb31a16c2..9cd4bd3032 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -595,8 +595,7 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec, } log_dhcp6_client(client, "Next retransmission in %s", - format_timespan(time_string, FORMAT_TIMESPAN_MAX, - client->retransmit_time, 0)); + format_timespan(time_string, FORMAT_TIMESPAN_MAX, client->retransmit_time, USEC_PER_SEC)); r = sd_event_add_time(client->event, &client->timeout_resend, clock_boottime_or_monotonic(), @@ -1048,9 +1047,7 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state) { timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t1) * USEC_PER_SEC); log_dhcp6_client(client, "T1 expires in %s", - format_timespan(time_string, - FORMAT_TIMESPAN_MAX, - timeout, 0)); + format_timespan(time_string, FORMAT_TIMESPAN_MAX, timeout, USEC_PER_SEC)); r = sd_event_add_time(client->event, &client->lease->ia.timeout_t1, @@ -1072,9 +1069,7 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state) { timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t2) * USEC_PER_SEC); log_dhcp6_client(client, "T2 expires in %s", - format_timespan(time_string, - FORMAT_TIMESPAN_MAX, - timeout, 0)); + format_timespan(time_string, FORMAT_TIMESPAN_MAX, timeout, USEC_PER_SEC)); r = sd_event_add_time(client->event, &client->lease->ia.timeout_t2, diff --git a/src/libsystemd-network/sd-icmp6-nd.c b/src/libsystemd-network/sd-icmp6-nd.c index bedcac8d9e..f014cac628 100644 --- a/src/libsystemd-network/sd-icmp6-nd.c +++ b/src/libsystemd-network/sd-icmp6-nd.c @@ -444,8 +444,7 @@ static int icmp6_ra_prefix_update(sd_icmp6_nd *nd, ssize_t len, log_icmp6_nd(nd, "New prefix "SD_ICMP6_ND_ADDRESS_FORMAT_STR"/%d lifetime %d expires in %s", SD_ICMP6_ND_ADDRESS_FORMAT_VAL(prefix->addr), prefix->len, lifetime, - format_timespan(time_string, FORMAT_TIMESPAN_MAX, - lifetime * USEC_PER_SEC, 0)); + format_timespan(time_string, FORMAT_TIMESPAN_MAX, lifetime * USEC_PER_SEC, USEC_PER_SEC)); LIST_PREPEND(prefixes, nd->prefixes, prefix); @@ -466,8 +465,7 @@ static int icmp6_ra_prefix_update(sd_icmp6_nd *nd, ssize_t len, log_icmp6_nd(nd, "Update prefix "SD_ICMP6_ND_ADDRESS_FORMAT_STR"/%d lifetime %d expires in %s", SD_ICMP6_ND_ADDRESS_FORMAT_VAL(prefix->addr), prefix->len, lifetime, - format_timespan(time_string, FORMAT_TIMESPAN_MAX, - lifetime * USEC_PER_SEC, 0)); + format_timespan(time_string, FORMAT_TIMESPAN_MAX, lifetime * USEC_PER_SEC, USEC_PER_SEC)); } r = icmp6_ra_prefix_set_timeout(nd, prefix, lifetime * USEC_PER_SEC); diff --git a/src/libsystemd-network/sd-ipv4ll.c b/src/libsystemd-network/sd-ipv4ll.c index dd427ddd78..57bd337a9a 100644 --- a/src/libsystemd-network/sd-ipv4ll.c +++ b/src/libsystemd-network/sd-ipv4ll.c @@ -25,6 +25,7 @@ #include <arpa/inet.h> #include "event-util.h" +#include "in-addr-util.h" #include "list.h" #include "random-util.h" #include "refcnt.h" @@ -232,6 +233,39 @@ bool sd_ipv4ll_is_running(sd_ipv4ll *ll) { return sd_ipv4acd_is_running(ll->acd); } +static bool ipv4ll_address_is_valid(const struct in_addr *address) { + uint32_t addr; + + assert(address); + + if (!in_addr_is_link_local(AF_INET, (const union in_addr_union *) address)) + return false; + + addr = be32toh(address->s_addr); + + if ((addr & 0x0000FF00) == 0x0000 || + (addr & 0x0000FF00) == 0xFF00) + return false; + + return true; +} + +int sd_ipv4ll_set_address(sd_ipv4ll *ll, const struct in_addr *address) { + int r; + + assert_return(ll, -EINVAL); + assert_return(address, -EINVAL); + assert_return(ipv4ll_address_is_valid(address), -EINVAL); + + r = sd_ipv4acd_set_address(ll->acd, address); + if (r < 0) + return r; + + ll->address = address->s_addr; + + return 0; +} + static int ipv4ll_pick_address(sd_ipv4ll *ll) { struct in_addr in_addr; be32_t addr; @@ -247,18 +281,15 @@ static int ipv4ll_pick_address(sd_ipv4ll *ll) { return r; addr = htonl((random & 0x0000FFFF) | IPV4LL_NETWORK); } while (addr == ll->address || - (ntohl(addr) & IPV4LL_NETMASK) != IPV4LL_NETWORK || (ntohl(addr) & 0x0000FF00) == 0x0000 || (ntohl(addr) & 0x0000FF00) == 0xFF00); in_addr.s_addr = addr; - r = sd_ipv4acd_set_address(ll->acd, &in_addr); + r = sd_ipv4ll_set_address(ll, &in_addr); if (r < 0) return r; - ll->address = addr; - return 0; } diff --git a/src/libsystemd-network/test-ipv4ll.c b/src/libsystemd-network/test-ipv4ll.c index e72204d992..b67a9f17d7 100644 --- a/src/libsystemd-network/test-ipv4ll.c +++ b/src/libsystemd-network/test-ipv4ll.c @@ -100,6 +100,7 @@ int arp_network_bind_raw_socket(int index, be32_t address, const struct ether_ad } static void test_public_api_setters(sd_event *e) { + struct in_addr address = {}; unsigned seed = 0; sd_ipv4ll *ll; struct ether_addr mac_addr = { @@ -118,6 +119,16 @@ static void test_public_api_setters(sd_event *e) { assert_se(sd_ipv4ll_set_callback(NULL, NULL, NULL) == -EINVAL); assert_se(sd_ipv4ll_set_callback(ll, NULL, NULL) == 0); + assert_se(sd_ipv4ll_set_address(ll, &address) == -EINVAL); + address.s_addr |= htobe32(169U << 24 | 254U << 16); + assert_se(sd_ipv4ll_set_address(ll, &address) == -EINVAL); + address.s_addr |= htobe32(0x00FF); + assert_se(sd_ipv4ll_set_address(ll, &address) == -EINVAL); + address.s_addr |= htobe32(0xF000); + assert_se(sd_ipv4ll_set_address(ll, &address) == 0); + address.s_addr |= htobe32(0x0F00); + assert_se(sd_ipv4ll_set_address(ll, &address) == -EINVAL); + assert_se(sd_ipv4ll_set_address_seed(NULL, seed) == -EINVAL); assert_se(sd_ipv4ll_set_address_seed(ll, seed) == 0); diff --git a/src/libsystemd/sd-daemon/sd-daemon.c b/src/libsystemd/sd-daemon/sd-daemon.c index 437518119b..ae534ba5b9 100644 --- a/src/libsystemd/sd-daemon/sd-daemon.c +++ b/src/libsystemd/sd-daemon/sd-daemon.c @@ -450,9 +450,11 @@ _public_ int sd_pid_notify_with_fds(pid_t pid, int unset_environment, const char if (n_fds > 0 || have_pid) { /* CMSG_SPACE(0) may return value different then zero, which results in miscalculated controllen. */ - msghdr.msg_controllen = (n_fds ? CMSG_SPACE(sizeof(int) * n_fds) : 0) + - CMSG_SPACE(sizeof(struct ucred)) * have_pid; - msghdr.msg_control = alloca(msghdr.msg_controllen); + msghdr.msg_controllen = + (n_fds > 0 ? CMSG_SPACE(sizeof(int) * n_fds) : 0) + + (have_pid ? CMSG_SPACE(sizeof(struct ucred)) : 0); + + msghdr.msg_control = alloca0(msghdr.msg_controllen); cmsg = CMSG_FIRSTHDR(&msghdr); if (n_fds > 0) { diff --git a/src/libsystemd/sd-netlink/netlink-internal.h b/src/libsystemd/sd-netlink/netlink-internal.h index 4026e2c341..b9cb80668d 100644 --- a/src/libsystemd/sd-netlink/netlink-internal.h +++ b/src/libsystemd/sd-netlink/netlink-internal.h @@ -64,6 +64,9 @@ struct sd_netlink { struct sockaddr_nl nl; } sockaddr; + Hashmap *broadcast_group_refs; + bool broadcast_group_dont_leave:1; /* until we can rely on 4.2 */ + sd_netlink_message **rqueue; unsigned rqueue_size; size_t rqueue_allocated; @@ -124,7 +127,8 @@ int message_new_empty(sd_netlink *rtnl, sd_netlink_message **ret); int socket_open(int family); int socket_bind(sd_netlink *nl); -int socket_join_broadcast_group(sd_netlink *nl, unsigned group); +int socket_broadcast_group_ref(sd_netlink *nl, unsigned group); +int socket_broadcast_group_unref(sd_netlink *nl, unsigned group); int socket_write_message(sd_netlink *nl, sd_netlink_message *m); int socket_read_message(sd_netlink *nl); diff --git a/src/libsystemd/sd-netlink/netlink-socket.c b/src/libsystemd/sd-netlink/netlink-socket.c index 84ff7c38c9..e1b14c3ed2 100644 --- a/src/libsystemd/sd-netlink/netlink-socket.c +++ b/src/libsystemd/sd-netlink/netlink-socket.c @@ -44,6 +44,65 @@ int socket_open(int family) { return fd; } +static int broadcast_groups_get(sd_netlink *nl) { + _cleanup_free_ uint32_t *groups = NULL; + socklen_t len = 0, old_len; + unsigned i, j; + int r; + + assert(nl); + assert(nl->fd > 0); + + r = getsockopt(nl->fd, SOL_NETLINK, NETLINK_LIST_MEMBERSHIPS, NULL, &len); + if (r < 0) { + if (errno == ENOPROTOOPT) { + nl->broadcast_group_dont_leave = true; + return 0; + } else + return -errno; + } + + if (len == 0) + return 0; + + groups = new0(uint32_t, len); + if (!groups) + return -ENOMEM; + + old_len = len; + + r = getsockopt(nl->fd, SOL_NETLINK, NETLINK_LIST_MEMBERSHIPS, groups, &len); + if (r < 0) + return -errno; + + if (old_len != len) + return -EIO; + + r = hashmap_ensure_allocated(&nl->broadcast_group_refs, NULL); + if (r < 0) + return r; + + for (i = 0; i < len; i++) { + for (j = 0; j < sizeof(uint32_t) * 8; j ++) { + uint32_t offset; + unsigned group; + + offset = 1U << j; + + if (!(groups[i] & offset)) + continue; + + group = i * sizeof(uint32_t) * 8 + j + 1; + + r = hashmap_put(nl->broadcast_group_refs, UINT_TO_PTR(group), UINT_TO_PTR(1)); + if (r < 0) + return r; + } + } + + return 0; +} + int socket_bind(sd_netlink *nl) { socklen_t addrlen; int r, one = 1; @@ -63,11 +122,32 @@ int socket_bind(sd_netlink *nl) { if (r < 0) return -errno; + r = broadcast_groups_get(nl); + if (r < 0) + return r; + return 0; } +static unsigned broadcast_group_get_ref(sd_netlink *nl, unsigned group) { + assert(nl); + + return PTR_TO_UINT(hashmap_get(nl->broadcast_group_refs, UINT_TO_PTR(group))); +} -int socket_join_broadcast_group(sd_netlink *nl, unsigned group) { +static int broadcast_group_set_ref(sd_netlink *nl, unsigned group, unsigned n_ref) { + int r; + + assert(nl); + + r = hashmap_replace(nl->broadcast_group_refs, UINT_TO_PTR(group), UINT_TO_PTR(n_ref)); + if (r < 0) + return r; + + return 0; +} + +static int broadcast_group_join(sd_netlink *nl, unsigned group) { int r; assert(nl); @@ -81,6 +161,79 @@ int socket_join_broadcast_group(sd_netlink *nl, unsigned group) { return 0; } +int socket_broadcast_group_ref(sd_netlink *nl, unsigned group) { + unsigned n_ref; + int r; + + assert(nl); + + n_ref = broadcast_group_get_ref(nl, group); + + n_ref ++; + + r = hashmap_ensure_allocated(&nl->broadcast_group_refs, NULL); + if (r < 0) + return r; + + r = broadcast_group_set_ref(nl, group, n_ref); + if (r < 0) + return r; + + if (n_ref > 1) + /* not yet in the group */ + return 0; + + r = broadcast_group_join(nl, group); + if (r < 0) + return r; + + return 0; +} + +static int broadcast_group_leave(sd_netlink *nl, unsigned group) { + int r; + + assert(nl); + assert(nl->fd >= 0); + assert(group > 0); + + if (nl->broadcast_group_dont_leave) + return 0; + + r = setsockopt(nl->fd, SOL_NETLINK, NETLINK_DROP_MEMBERSHIP, &group, sizeof(group)); + if (r < 0) + return -errno; + + return 0; +} + +int socket_broadcast_group_unref(sd_netlink *nl, unsigned group) { + unsigned n_ref; + int r; + + assert(nl); + + n_ref = broadcast_group_get_ref(nl, group); + + assert(n_ref > 0); + + n_ref --; + + r = broadcast_group_set_ref(nl, group, n_ref); + if (r < 0) + return r; + + if (n_ref > 0) + /* still refs left */ + return 0; + + r = broadcast_group_leave(nl, group); + if (r < 0) + return r; + + return 0; +} + /* returns the number of bytes sent, or a negative error code */ int socket_write_message(sd_netlink *nl, sd_netlink_message *m) { union { diff --git a/src/libsystemd/sd-netlink/rtnl-message.c b/src/libsystemd/sd-netlink/rtnl-message.c index 2f31f4ee69..03049bd31f 100644 --- a/src/libsystemd/sd-netlink/rtnl-message.c +++ b/src/libsystemd/sd-netlink/rtnl-message.c @@ -99,6 +99,66 @@ int sd_rtnl_message_route_get_family(sd_netlink_message *m, int *family) { return 0; } +int sd_rtnl_message_route_get_protocol(sd_netlink_message *m, unsigned char *protocol) { + 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(protocol, -EINVAL); + + rtm = NLMSG_DATA(m->hdr); + + *protocol = rtm->rtm_protocol; + + return 0; +} + +int sd_rtnl_message_route_get_scope(sd_netlink_message *m, unsigned char *scope) { + 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(scope, -EINVAL); + + rtm = NLMSG_DATA(m->hdr); + + *scope = rtm->rtm_scope; + + return 0; +} + +int sd_rtnl_message_route_get_tos(sd_netlink_message *m, unsigned char *tos) { + 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(tos, -EINVAL); + + rtm = NLMSG_DATA(m->hdr); + + *tos = rtm->rtm_tos; + + return 0; +} + +int sd_rtnl_message_route_get_table(sd_netlink_message *m, unsigned char *table) { + 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(table, -EINVAL); + + rtm = NLMSG_DATA(m->hdr); + + *table = rtm->rtm_table; + + return 0; +} + int sd_rtnl_message_route_get_dst_prefixlen(sd_netlink_message *m, unsigned char *dst_len) { struct rtmsg *rtm; diff --git a/src/libsystemd/sd-netlink/sd-netlink.c b/src/libsystemd/sd-netlink/sd-netlink.c index d248869c8d..5af28600ba 100644 --- a/src/libsystemd/sd-netlink/sd-netlink.c +++ b/src/libsystemd/sd-netlink/sd-netlink.c @@ -183,10 +183,11 @@ sd_netlink *sd_netlink_unref(sd_netlink *rtnl) { sd_event_unref(rtnl->event); while ((f = rtnl->match_callbacks)) { - LIST_REMOVE(match_callbacks, rtnl->match_callbacks, f); - free(f); + sd_netlink_remove_match(rtnl, f->type, f->callback, f->userdata); } + hashmap_free(rtnl->broadcast_group_refs); + safe_close(rtnl->fd); free(rtnl); } @@ -856,25 +857,32 @@ int sd_netlink_add_match(sd_netlink *rtnl, switch (type) { case RTM_NEWLINK: - case RTM_SETLINK: - case RTM_GETLINK: case RTM_DELLINK: - r = socket_join_broadcast_group(rtnl, RTNLGRP_LINK); + r = socket_broadcast_group_ref(rtnl, RTNLGRP_LINK); if (r < 0) return r; break; case RTM_NEWADDR: - case RTM_GETADDR: case RTM_DELADDR: - r = socket_join_broadcast_group(rtnl, RTNLGRP_IPV4_IFADDR); + r = socket_broadcast_group_ref(rtnl, RTNLGRP_IPV4_IFADDR); if (r < 0) return r; - r = socket_join_broadcast_group(rtnl, RTNLGRP_IPV6_IFADDR); + r = socket_broadcast_group_ref(rtnl, RTNLGRP_IPV6_IFADDR); + if (r < 0) + return r; + + break; + case RTM_NEWROUTE: + case RTM_DELROUTE: + r = socket_broadcast_group_ref(rtnl, RTNLGRP_IPV4_ROUTE); if (r < 0) return r; + r = socket_broadcast_group_ref(rtnl, RTNLGRP_IPV6_ROUTE); + if (r < 0) + return r; break; default: return -EOPNOTSUPP; @@ -892,23 +900,50 @@ int sd_netlink_remove_match(sd_netlink *rtnl, sd_netlink_message_handler_t callback, void *userdata) { struct match_callback *c; + int r; assert_return(rtnl, -EINVAL); assert_return(callback, -EINVAL); assert_return(!rtnl_pid_changed(rtnl), -ECHILD); - /* we should unsubscribe from the broadcast groups at this point, but it is not so - trivial for a few reasons: the refcounting is a bit of a mess and not obvious - how it will look like after we add genetlink support, and it is also not possible - to query what broadcast groups were subscribed to when we inherit the socket to get - the initial refcount. The latter could indeed be done for the first 32 broadcast - groups (which incidentally is all we currently support in .socket units anyway), - but we better not rely on only ever using 32 groups. */ LIST_FOREACH(match_callbacks, c, rtnl->match_callbacks) if (c->callback == callback && c->type == type && c->userdata == userdata) { LIST_REMOVE(match_callbacks, rtnl->match_callbacks, c); free(c); + switch (type) { + case RTM_NEWLINK: + case RTM_DELLINK: + r = socket_broadcast_group_unref(rtnl, RTNLGRP_LINK); + if (r < 0) + return r; + + break; + case RTM_NEWADDR: + case RTM_DELADDR: + r = socket_broadcast_group_unref(rtnl, RTNLGRP_IPV4_IFADDR); + if (r < 0) + return r; + + r = socket_broadcast_group_unref(rtnl, RTNLGRP_IPV6_IFADDR); + if (r < 0) + return r; + + break; + case RTM_NEWROUTE: + case RTM_DELROUTE: + r = socket_broadcast_group_unref(rtnl, RTNLGRP_IPV4_ROUTE); + if (r < 0) + return r; + + r = socket_broadcast_group_unref(rtnl, RTNLGRP_IPV6_ROUTE); + if (r < 0) + return r; + break; + default: + return -EOPNOTSUPP; + } + return 1; } diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c index 40d587ddb8..aeedf68e77 100644 --- a/src/login/logind-dbus.c +++ b/src/login/logind-dbus.c @@ -2588,7 +2588,7 @@ int match_job_removed(sd_bus_message *message, void *userdata, sd_bus_error *err } if (m->action_job && streq(m->action_job, path)) { - log_info("Operation finished."); + log_info("Operation '%s' finished.", inhibit_what_to_string(m->action_what)); /* Tell people that they now may take a lock again */ send_prepare_for(m, m->action_what, false); diff --git a/src/network/networkd-address-pool.c b/src/network/networkd-address-pool.c index d609daafde..b3450c1456 100644 --- a/src/network/networkd-address-pool.c +++ b/src/network/networkd-address-pool.c @@ -21,6 +21,7 @@ #include "networkd.h" #include "networkd-address-pool.h" +#include "set.h" int address_pool_new( Manager *m, @@ -96,9 +97,10 @@ static bool address_pool_prefix_is_taken( HASHMAP_FOREACH(l, p->manager->links, i) { Address *a; + Iterator j; /* Don't clash with assigned addresses */ - LIST_FOREACH(addresses, a, l->addresses) { + SET_FOREACH(a, l->addresses, j) { if (a->family != p->family) continue; diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c index 388beb5d4c..316ae2e4cb 100644 --- a/src/network/networkd-address.c +++ b/src/network/networkd-address.c @@ -21,26 +21,37 @@ #include <net/if.h> -#include "utf8.h" -#include "util.h" #include "conf-parser.h" #include "firewall-util.h" #include "netlink-util.h" +#include "set.h" +#include "utf8.h" +#include "util.h" #include "networkd.h" #include "networkd-address.h" -static void address_init(Address *address) { - assert(address); +int address_new(Address **ret) { + _cleanup_address_free_ Address *address = NULL; + + address = new0(Address, 1); + if (!address) + return -ENOMEM; address->family = AF_UNSPEC; address->scope = RT_SCOPE_UNIVERSE; address->cinfo.ifa_prefered = CACHE_INFO_INFINITY_LIFE_TIME; address->cinfo.ifa_valid = CACHE_INFO_INFINITY_LIFE_TIME; + + *ret = address; + address = NULL; + + return 0; } int address_new_static(Network *network, unsigned section, Address **ret) { _cleanup_address_free_ Address *address = NULL; + int r; if (section) { address = hashmap_get(network->addresses_by_section, UINT_TO_PTR(section)); @@ -52,11 +63,9 @@ int address_new_static(Network *network, unsigned section, Address **ret) { } } - address = new0(Address, 1); - if (!address) - return -ENOMEM; - - address_init(address); + r = address_new(&address); + if (r < 0) + return r; address->network = network; @@ -74,21 +83,6 @@ int address_new_static(Network *network, unsigned section, Address **ret) { return 0; } -int address_new_dynamic(Address **ret) { - _cleanup_address_free_ Address *address = NULL; - - address = new0(Address, 1); - if (!address) - return -ENOMEM; - - address_init(address); - - *ret = address; - address = NULL; - - return 0; -} - void address_free(Address *address) { if (!address) return; @@ -101,10 +95,110 @@ void address_free(Address *address) { UINT_TO_PTR(address->section)); } + if (address->link) + set_remove(address->link->addresses, address); + free(address); } -int address_establish(Address *address, Link *link) { +static void address_hash_func(const void *b, struct siphash *state) { + const Address *a = b; + + assert(a); + + siphash24_compress(&a->family, sizeof(a->family), state); + + switch (a->family) { + case AF_INET: + siphash24_compress(&a->prefixlen, sizeof(a->prefixlen), state); + + /* peer prefix */ + if (a->prefixlen != 0) { + uint32_t prefix; + + if (a->in_addr_peer.in.s_addr != 0) + prefix = be32toh(a->in_addr_peer.in.s_addr) >> (32 - a->prefixlen); + else + prefix = be32toh(a->in_addr.in.s_addr) >> (32 - a->prefixlen); + + siphash24_compress(&prefix, sizeof(prefix), state); + } + + /* fallthrough */ + case AF_INET6: + /* local address */ + siphash24_compress(&a->in_addr, FAMILY_ADDRESS_SIZE(a->family), state); + + break; + default: + /* treat any other address family as AF_UNSPEC */ + break; + } +} + +static int address_compare_func(const void *c1, const void *c2) { + const Address *a1 = c1, *a2 = c2; + + if (a1->family < a2->family) + return -1; + if (a1->family > a2->family) + return 1; + + switch (a1->family) { + /* use the same notion of equality as the kernel does */ + case AF_INET: + if (a1->prefixlen < a2->prefixlen) + return -1; + if (a1->prefixlen > a2->prefixlen) + return 1; + + /* compare the peer prefixes */ + if (a1->prefixlen != 0) { + /* make sure we don't try to shift by 32. + * See ISO/IEC 9899:TC3 § 6.5.7.3. */ + uint32_t b1, b2; + + if (a1->in_addr_peer.in.s_addr != 0) + b1 = be32toh(a1->in_addr_peer.in.s_addr) >> (32 - a1->prefixlen); + else + b1 = be32toh(a1->in_addr.in.s_addr) >> (32 - a1->prefixlen); + + if (a2->in_addr_peer.in.s_addr != 0) + b2 = be32toh(a2->in_addr_peer.in.s_addr) >> (32 - a1->prefixlen); + else + b2 = be32toh(a2->in_addr.in.s_addr) >> (32 - a1->prefixlen); + + if (b1 < b2) + return -1; + if (b1 > b2) + return 1; + } + + /* fall-through */ + case AF_INET6: + return memcmp(&a1->in_addr, &a2->in_addr, FAMILY_ADDRESS_SIZE(a1->family)); + default: + /* treat any other address family as AF_UNSPEC */ + return 0; + } +} + +static const struct hash_ops address_hash_ops = { + .hash = address_hash_func, + .compare = address_compare_func +}; + +bool address_equal(Address *a1, Address *a2) { + if (a1 == a2) + return true; + + if (!a1 || !a2) + return false; + + return address_compare_func(a1, a2) == 0; +} + +static int address_establish(Address *address, Link *link) { bool masq; int r; @@ -131,7 +225,43 @@ int address_establish(Address *address, Link *link) { return 0; } -int address_release(Address *address, Link *link) { +int address_add(Link *link, int family, const union in_addr_union *in_addr, unsigned char prefixlen, Address **ret) { + _cleanup_address_free_ Address *address = NULL; + int r; + + assert(link); + assert(in_addr); + assert(ret); + + r = address_new(&address); + if (r < 0) + return r; + + address->family = family; + address->in_addr = *in_addr; + address->prefixlen = prefixlen; + + r = set_ensure_allocated(&link->addresses, &address_hash_ops); + if (r < 0) + return r; + + r = set_put(link->addresses, address); + if (r < 0) + return r; + + address->link = link; + + r = address_establish(address, link); + if (r < 0) + return r; + + *ret = address; + address = NULL; + + return 0; +} + +static int address_release(Address *address, Link *link) { int r; assert(address); @@ -152,7 +282,36 @@ int address_release(Address *address, Link *link) { return 0; } -int address_drop(Address *address, Link *link, +int address_drop(Address *address) { + assert(address); + + address_release(address, address->link); + address_free(address); + + return 0; +} + +int address_get(Link *link, int family, const union in_addr_union *in_addr, unsigned char prefixlen, Address **ret) { + Address address = {}, *existing; + + assert(link); + assert(in_addr); + assert(ret); + + address.family = family; + address.in_addr = *in_addr; + address.prefixlen = prefixlen; + + existing = set_get(link->addresses, &address); + if (!existing) + return -ENOENT; + + *ret = existing; + + return 0; +} + +int address_remove(Address *address, Link *link, sd_netlink_message_handler_t callback) { _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL; int r; @@ -292,7 +451,7 @@ static int address_acquire(Link *link, Address *original, Address **ret) { } else if (original->family == AF_INET6) in_addr.in6.s6_addr[15] |= 1; - r = address_new_dynamic(&na); + r = address_new(&na); if (r < 0) return r; @@ -580,49 +739,8 @@ int config_parse_label(const char *unit, return 0; } -bool address_equal(Address *a1, Address *a2) { - /* same object */ - if (a1 == a2) - return true; - - /* one, but not both, is NULL */ - if (!a1 || !a2) - return false; - - if (a1->family != a2->family) - return false; - - switch (a1->family) { - /* use the same notion of equality as the kernel does */ - case AF_UNSPEC: - return true; - - case AF_INET: - if (a1->prefixlen != a2->prefixlen) - return false; - else if (a1->prefixlen == 0) - /* make sure we don't try to shift by 32. - * See ISO/IEC 9899:TC3 § 6.5.7.3. */ - return true; - else { - uint32_t b1, b2; - - b1 = be32toh(a1->in_addr.in.s_addr); - b2 = be32toh(a2->in_addr.in.s_addr); - - return (b1 >> (32 - a1->prefixlen)) == (b2 >> (32 - a1->prefixlen)); - } - - case AF_INET6: { - uint64_t *b1, *b2; - - b1 = (uint64_t*)&a1->in_addr.in6; - b2 = (uint64_t*)&a2->in_addr.in6; +bool address_is_ready(const Address *a) { + assert(a); - return (((b1[0] ^ b2[0]) | (b1[1] ^ b2[1])) == 0UL); - } - - default: - assert_not_reached("Invalid address family"); - } + return !(a->flags & (IFA_F_TENTATIVE | IFA_F_DEPRECATED)); } diff --git a/src/network/networkd-address.h b/src/network/networkd-address.h index 39789a2382..425344fe48 100644 --- a/src/network/networkd-address.h +++ b/src/network/networkd-address.h @@ -38,6 +38,8 @@ struct Address { Network *network; unsigned section; + Link *link; + int family; unsigned char prefixlen; unsigned char scope; @@ -56,14 +58,16 @@ struct Address { }; int address_new_static(Network *network, unsigned section, Address **ret); -int address_new_dynamic(Address **ret); +int address_new(Address **ret); void address_free(Address *address); +int address_add(Link *link, int family, const union in_addr_union *in_addr, unsigned char prefixlen, Address **ret); +int address_get(Link *link, int family, const union in_addr_union *in_addr, unsigned char prefixlen, Address **ret); +int address_drop(Address *address); int address_configure(Address *address, Link *link, sd_netlink_message_handler_t callback); int address_update(Address *address, Link *link, sd_netlink_message_handler_t callback); -int address_drop(Address *address, Link *link, sd_netlink_message_handler_t callback); -int address_establish(Address *address, Link *link); -int address_release(Address *address, Link *link); +int address_remove(Address *address, Link *link, sd_netlink_message_handler_t callback); bool address_equal(Address *a1, Address *a2); +bool address_is_ready(const Address *a); DEFINE_TRIVIAL_CLEANUP_FUNC(Address*, address_free); #define _cleanup_address_free_ _cleanup_(address_freep) diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c index 04f04df117..5d9bfcea7c 100644 --- a/src/network/networkd-dhcp4.c +++ b/src/network/networkd-dhcp4.c @@ -72,11 +72,11 @@ static int link_set_dhcp_routes(Link *link) { if (r < 0) return log_link_warning_errno(link, r, "DHCP error: could not get address: %m"); - r = route_new_dynamic(&route, RTPROT_DHCP); + r = route_new(&route, RTPROT_DHCP); if (r < 0) return log_link_error_errno(link, r, "Could not allocate route: %m"); - r = route_new_dynamic(&route_gw, RTPROT_DHCP); + r = route_new(&route_gw, RTPROT_DHCP); if (r < 0) return log_link_error_errno(link, r, "Could not allocate route: %m"); @@ -120,7 +120,7 @@ static int link_set_dhcp_routes(Link *link) { for (i = 0; i < n; i++) { _cleanup_route_free_ Route *route = NULL; - r = route_new_dynamic(&route, RTPROT_DHCP); + r = route_new(&route, RTPROT_DHCP); if (r < 0) return log_link_error_errno(link, r, "Could not allocate route: %m"); @@ -162,45 +162,45 @@ static int dhcp_lease_lost(Link *link) { for (i = 0; i < n; i++) { _cleanup_route_free_ Route *route = NULL; - r = route_new_dynamic(&route, RTPROT_UNSPEC); + r = route_new(&route, RTPROT_UNSPEC); if (r >= 0) { route->family = AF_INET; route->in_addr.in = routes[i].gw_addr; route->dst_addr.in = routes[i].dst_addr; route->dst_prefixlen = routes[i].dst_prefixlen; - route_drop(route, link, - &link_route_drop_handler); + route_remove(route, link, + &link_route_remove_handler); } } } } - r = address_new_dynamic(&address); + r = address_new(&address); if (r >= 0) { r = sd_dhcp_lease_get_router(link->dhcp_lease, &gateway); if (r >= 0) { _cleanup_route_free_ Route *route_gw = NULL; _cleanup_route_free_ Route *route = NULL; - r = route_new_dynamic(&route_gw, RTPROT_UNSPEC); + r = route_new(&route_gw, RTPROT_UNSPEC); if (r >= 0) { route_gw->family = AF_INET; route_gw->dst_addr.in = gateway; route_gw->dst_prefixlen = 32; route_gw->scope = RT_SCOPE_LINK; - route_drop(route_gw, link, - &link_route_drop_handler); + route_remove(route_gw, link, + &link_route_remove_handler); } - r = route_new_dynamic(&route, RTPROT_UNSPEC); + r = route_new(&route, RTPROT_UNSPEC); if (r >= 0) { route->family = AF_INET; route->in_addr.in = gateway; - route_drop(route, link, - &link_route_drop_handler); + route_remove(route, link, + &link_route_remove_handler); } } @@ -214,7 +214,7 @@ static int dhcp_lease_lost(Link *link) { address->in_addr.in = addr; address->prefixlen = prefixlen; - address_drop(address, link, &link_address_drop_handler); + address_remove(address, link, &link_address_remove_handler); } } @@ -267,7 +267,7 @@ static int dhcp4_address_handler(sd_netlink *rtnl, sd_netlink_message *m, log_link_error_errno(link, r, "Could not set DHCPv4 address: %m"); link_enter_failed(link); } else if (r >= 0) - link_rtnl_process_address(rtnl, m, link->manager); + manager_rtnl_process_address(rtnl, m, link->manager); link_set_dhcp_routes(link); @@ -288,7 +288,7 @@ static int dhcp4_update_address(Link *link, prefixlen = in_addr_netmask_to_prefixlen(netmask); - r = address_new_dynamic(&addr); + r = address_new(&addr); if (r < 0) return r; diff --git a/src/network/networkd-dhcp6.c b/src/network/networkd-dhcp6.c index 3cb7b8d9ca..2f9ecf7a89 100644 --- a/src/network/networkd-dhcp6.c +++ b/src/network/networkd-dhcp6.c @@ -58,7 +58,7 @@ static int dhcp6_address_handler(sd_netlink *rtnl, sd_netlink_message *m, link_enter_failed(link); } else if (r >= 0) - link_rtnl_process_address(rtnl, m, link->manager); + manager_rtnl_process_address(rtnl, m, link->manager); return 1; } @@ -69,7 +69,7 @@ static int dhcp6_address_update(Link *link, struct in6_addr *ip6_addr, int r; _cleanup_address_free_ Address *addr = NULL; - r = address_new_dynamic(&addr); + r = address_new(&addr); if (r < 0) return r; diff --git a/src/network/networkd-ipv4ll.c b/src/network/networkd-ipv4ll.c index 1902b3d23a..01ee9f9f4a 100644 --- a/src/network/networkd-ipv4ll.c +++ b/src/network/networkd-ipv4ll.c @@ -42,7 +42,7 @@ static int ipv4ll_address_lost(Link *link) { log_link_debug(link, "IPv4 link-local release %u.%u.%u.%u", ADDRESS_FMT_VAL(addr)); - r = address_new_dynamic(&address); + r = address_new(&address); if (r < 0) { log_link_error_errno(link, r, "Could not allocate address: %m"); return r; @@ -53,9 +53,9 @@ static int ipv4ll_address_lost(Link *link) { address->prefixlen = 16; address->scope = RT_SCOPE_LINK; - address_drop(address, link, &link_address_drop_handler); + address_remove(address, link, &link_address_remove_handler); - r = route_new_dynamic(&route, RTPROT_UNSPEC); + r = route_new(&route, RTPROT_UNSPEC); if (r < 0) { log_link_error_errno(link, r, "Could not allocate route: %m"); return r; @@ -65,7 +65,7 @@ static int ipv4ll_address_lost(Link *link) { route->scope = RT_SCOPE_LINK; route->metrics = IPV4LL_ROUTE_METRIC; - route_drop(route, link, &link_route_drop_handler); + route_remove(route, link, &link_route_remove_handler); link_client_handler(link); @@ -105,7 +105,7 @@ static int ipv4ll_address_handler(sd_netlink *rtnl, sd_netlink_message *m, void log_link_error_errno(link, r, "could not set ipv4ll address: %m"); link_enter_failed(link); } else if (r >= 0) - link_rtnl_process_address(rtnl, m, link->manager); + manager_rtnl_process_address(rtnl, m, link->manager); link->ipv4ll_address = true; @@ -133,7 +133,7 @@ static int ipv4ll_address_claimed(sd_ipv4ll *ll, Link *link) { log_link_debug(link, "IPv4 link-local claim %u.%u.%u.%u", ADDRESS_FMT_VAL(address)); - r = address_new_dynamic(&ll_addr); + r = address_new(&ll_addr); if (r < 0) return r; @@ -149,7 +149,7 @@ static int ipv4ll_address_claimed(sd_ipv4ll *ll, Link *link) { link->ipv4ll_address = false; - r = route_new_dynamic(&route, RTPROT_STATIC); + r = route_new(&route, RTPROT_STATIC); if (r < 0) return r; diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index ffc9578e86..f6cc1f8ee2 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -23,15 +23,16 @@ #include <linux/if.h> #include <unistd.h> -#include "util.h" -#include "virt.h" -#include "fileio.h" -#include "socket-util.h" #include "bus-util.h" -#include "udev-util.h" -#include "netlink-util.h" #include "dhcp-lease-internal.h" +#include "fileio.h" +#include "netlink-util.h" #include "network-internal.h" +#include "set.h" +#include "socket-util.h" +#include "udev-util.h" +#include "util.h" +#include "virt.h" #include "networkd-link.h" #include "networkd-netdev.h" @@ -291,10 +292,10 @@ static void link_free(Link *link) { if (!link) return; - while ((address = link->addresses)) { - LIST_REMOVE(addresses, link->addresses, address); - address_free(address); - } + while (!set_isempty(link->addresses)) + address_free(set_first(link->addresses)); + + set_free(link->addresses); while ((address = link->pool_addresses)) { LIST_REMOVE(addresses, link->pool_addresses, address); @@ -336,15 +337,28 @@ static void link_free(Link *link) { } Link *link_unref(Link *link) { - if (link && (-- link->n_ref <= 0)) - link_free(link); + if (!link) + return NULL; + + assert(link->n_ref > 0); + + link->n_ref --; + + if (link->n_ref > 0) + return NULL; + + link_free(link); return NULL; } Link *link_ref(Link *link) { - if (link) - assert_se(++ link->n_ref >= 2); + if (!link) + return NULL; + + assert(link->n_ref > 0); + + link->n_ref ++; return link; } @@ -531,7 +545,7 @@ static int route_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata r = sd_netlink_message_get_errno(m); if (r < 0 && r != -EEXIST) - log_link_warning_errno(link, r, "%-*s: could not set route: %m", IFNAMSIZ, link->ifname); + log_link_warning_errno(link, r, "Could not set route: %m"); if (link->link_messages == 0) { log_link_debug(link, "Routes set"); @@ -572,7 +586,7 @@ static int link_enter_set_routes(Link *link) { return 0; } -int link_route_drop_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { +int link_route_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { _cleanup_link_unref_ Link *link = userdata; int r; @@ -585,7 +599,7 @@ int link_route_drop_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userd r = sd_netlink_message_get_errno(m); if (r < 0 && r != -ESRCH) - log_link_warning_errno(link, r, "%-*s: could not drop route: %m", IFNAMSIZ, link->ifname); + log_link_warning_errno(link, r, "Could not drop route: %m"); return 1; } @@ -609,9 +623,9 @@ static int address_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userda r = sd_netlink_message_get_errno(m); if (r < 0 && r != -EEXIST) - log_link_warning_errno(link, r, "%-*s: could not set address: %m", IFNAMSIZ, link->ifname); + log_link_warning_errno(link, r, "could not set address: %m"); else if (r >= 0) - link_rtnl_process_address(rtnl, m, link->manager); + manager_rtnl_process_address(rtnl, m, link->manager); if (link->link_messages == 0) { log_link_debug(link, "Addresses set"); @@ -854,7 +868,7 @@ static int link_enter_set_addresses(Link *link) { return 0; } -int link_address_drop_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { +int link_address_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { _cleanup_link_unref_ Link *link = userdata; int r; @@ -867,7 +881,7 @@ int link_address_drop_handler(sd_netlink *rtnl, sd_netlink_message *m, void *use r = sd_netlink_message_get_errno(m); if (r < 0 && r != -EADDRNOTAVAIL) - log_link_warning_errno(link, r, "%-*s: could not drop address: %m", IFNAMSIZ, link->ifname); + log_link_warning_errno(link, r, "Could not drop address: %m"); return 1; } @@ -1019,7 +1033,7 @@ static int set_mtu_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userda r = sd_netlink_message_get_errno(m); if (r < 0) - log_link_warning_errno(link, r, "%-*s: could not set MTU: %m", IFNAMSIZ, link->ifname); + log_link_warning_errno(link, r, "Could not set MTU: %m"); return 1; } @@ -1207,7 +1221,7 @@ static int link_up_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userda if (r < 0) /* we warn but don't fail the link, as it may be brought up later */ - log_link_warning_errno(link, r, "%-*s: could not bring up interface: %m", IFNAMSIZ, link->ifname); + log_link_warning_errno(link, r, "Could not bring up interface: %m"); return 1; } @@ -1294,7 +1308,7 @@ static int link_down_handler(sd_netlink *rtnl, sd_netlink_message *m, void *user r = sd_netlink_message_get_errno(m); if (r < 0) - log_link_warning_errno(link, r, "%-*s: could not bring down interface: %m", IFNAMSIZ, link->ifname); + log_link_warning_errno(link, r, "Could not bring down interface: %m"); return 1; } @@ -1616,7 +1630,7 @@ static int netdev_join_handler(sd_netlink *rtnl, sd_netlink_message *m, void *us r = sd_netlink_message_get_errno(m); if (r < 0 && r != -EEXIST) { - log_link_error_errno(link, r, "%-*s: could not join netdev: %m", IFNAMSIZ, link->ifname); + log_link_error_errno(link, r, "Could not join netdev: %m"); link_enter_failed(link); return 1; } else @@ -1812,16 +1826,16 @@ static int link_set_ipv6_accept_ra(Link *link) { * disabled if local forwarding is enabled). * If set, ignore or enforce RA independent of local forwarding state. */ - if (link->network->ipv6_accept_ra < 0) { + 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) { + else if (link->network->ipv6_accept_ra > 0) /* "2" means accept RA even if ip_forward is enabled */ v = "2"; - } else { + else /* "0" means ignore RA */ v = "0"; - } + p = strjoina("/proc/sys/net/ipv6/conf/", link->ifname, "/accept_ra"); r = write_string_file(p, v, 0); @@ -2011,179 +2025,6 @@ int link_initialized(Link *link, struct udev_device *device) { return 0; } -static Address* link_get_equal_address(Link *link, Address *needle) { - Address *i; - - assert(link); - assert(needle); - - LIST_FOREACH(addresses, i, link->addresses) - if (address_equal(i, needle)) - return i; - - return NULL; -} - -int link_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message, void *userdata) { - Manager *m = userdata; - Link *link = NULL; - uint16_t type; - _cleanup_address_free_ Address *address = NULL; - unsigned char flags; - Address *existing; - char buf[INET6_ADDRSTRLEN], valid_buf[FORMAT_TIMESPAN_MAX]; - const char *valid_str = NULL; - int r, ifindex; - - assert(rtnl); - assert(message); - assert(m); - - if (sd_netlink_message_is_error(message)) { - r = sd_netlink_message_get_errno(message); - if (r < 0) - log_warning_errno(r, "rtnl: failed to receive address: %m"); - - return 0; - } - - r = sd_netlink_message_get_type(message, &type); - if (r < 0) { - log_warning_errno(r, "rtnl: could not get message type: %m"); - return 0; - } else if (type != RTM_NEWADDR && type != RTM_DELADDR) { - log_warning("rtnl: received unexpected message type when processing address"); - return 0; - } - - r = sd_rtnl_message_addr_get_ifindex(message, &ifindex); - if (r < 0) { - log_warning_errno(r, "rtnl: could not get ifindex from address: %m"); - return 0; - } else if (ifindex <= 0) { - log_warning("rtnl: received address message with invalid ifindex: %d", ifindex); - return 0; - } else { - r = link_get(m, ifindex, &link); - if (r < 0 || !link) { - /* when enumerating we might be out of sync, but we will - * get the address again, so just ignore it */ - if (!m->enumerating) - log_warning("rtnl: received address for nonexistent link (%d), ignoring", ifindex); - return 0; - } - } - - r = address_new_dynamic(&address); - if (r < 0) - return r; - - r = sd_rtnl_message_addr_get_family(message, &address->family); - if (r < 0 || !IN_SET(address->family, AF_INET, AF_INET6)) { - log_link_warning(link, "rtnl: received address with invalid family, ignoring."); - return 0; - } - - r = sd_rtnl_message_addr_get_prefixlen(message, &address->prefixlen); - if (r < 0) { - log_link_warning_errno(link, r, "rtnl: received address with invalid prefixlen, ignoring: %m"); - return 0; - } - - r = sd_rtnl_message_addr_get_scope(message, &address->scope); - if (r < 0) { - log_link_warning_errno(link, r, "rtnl: received address with invalid scope, ignoring: %m"); - return 0; - } - - r = sd_rtnl_message_addr_get_flags(message, &flags); - if (r < 0) { - log_link_warning_errno(link, r, "rtnl: received address with invalid flags, ignoring: %m"); - return 0; - } - address->flags = flags; - - switch (address->family) { - case AF_INET: - r = sd_netlink_message_read_in_addr(message, IFA_LOCAL, &address->in_addr.in); - if (r < 0) { - log_link_warning_errno(link, r, "rtnl: received address without valid address, ignoring: %m"); - return 0; - } - - break; - - case AF_INET6: - r = sd_netlink_message_read_in6_addr(message, IFA_ADDRESS, &address->in_addr.in6); - if (r < 0) { - log_link_warning_errno(link, r, "rtnl: received address without valid address, ignoring: %m"); - return 0; - } - - break; - - default: - assert_not_reached("invalid address family"); - } - - if (!inet_ntop(address->family, &address->in_addr, buf, INET6_ADDRSTRLEN)) { - log_link_warning(link, "Could not print address"); - return 0; - } - - r = sd_netlink_message_read_cache_info(message, IFA_CACHEINFO, &address->cinfo); - if (r >= 0) { - if (address->cinfo.ifa_valid == CACHE_INFO_INFINITY_LIFE_TIME) - valid_str = "ever"; - else - valid_str = format_timespan(valid_buf, FORMAT_TIMESPAN_MAX, - address->cinfo.ifa_valid * USEC_PER_SEC, - USEC_PER_SEC); - } - - existing = link_get_equal_address(link, address); - - switch (type) { - case RTM_NEWADDR: - if (existing) { - log_link_debug(link, "Updating address: %s/%u (valid for %s)", buf, address->prefixlen, valid_str); - - - existing->scope = address->scope; - existing->flags = address->flags; - existing->cinfo = address->cinfo; - - } else { - log_link_debug(link, "Adding address: %s/%u (valid for %s)", buf, address->prefixlen, valid_str); - - LIST_PREPEND(addresses, link->addresses, address); - address_establish(address, link); - - address = NULL; - - link_save(link); - } - - break; - - case RTM_DELADDR: - - if (existing) { - log_link_debug(link, "Removing address: %s/%u (valid for %s)", buf, address->prefixlen, valid_str); - address_release(existing, link); - LIST_REMOVE(addresses, link->addresses, existing); - address_free(existing); - } else - log_link_warning(link, "Removing non-existent address: %s/%u (valid for %s)", buf, address->prefixlen, valid_str); - - break; - default: - assert_not_reached("Received invalid RTNL message type"); - } - - return 1; -} - int link_add(Manager *m, sd_netlink_message *message, Link **ret) { Link *link; _cleanup_udev_device_unref_ struct udev_device *device = NULL; @@ -2207,8 +2048,10 @@ int link_add(Manager *m, sd_netlink_message *message, Link **ret) { /* not in a container, udev will be around */ sprintf(ifindex_str, "n%d", link->ifindex); device = udev_device_new_from_device_id(m->udev, ifindex_str); - if (!device) - return log_link_warning_errno(link, errno, "Could not find udev device: %m"); + if (!device) { + r = log_link_warning_errno(link, errno, "Could not find udev device: %m"); + goto failed; + } if (udev_device_get_is_initialized(device) <= 0) { /* not yet ready */ @@ -2218,17 +2061,20 @@ int link_add(Manager *m, sd_netlink_message *message, Link **ret) { r = link_initialized(link, device); if (r < 0) - return r; + goto failed; } else { /* we are calling a callback directly, so must take a ref */ link_ref(link); r = link_initialized_and_synced(m->rtnl, NULL, link); if (r < 0) - return r; + goto failed; } return 0; +failed: + link_enter_failed(link); + return r; } static int link_carrier_gained(Link *link) { @@ -2425,10 +2271,11 @@ static void link_update_operstate(Link *link) { else if (link_has_carrier(link)) { Address *address; uint8_t scope = RT_SCOPE_NOWHERE; + Iterator i; /* if we have carrier, check what addresses we have */ - LIST_FOREACH(addresses, address, link->addresses) { - if (address->flags & (IFA_F_TENTATIVE | IFA_F_DEPRECATED)) + SET_FOREACH(address, link->addresses, i) { + if (!address_is_ready(address)) continue; if (address->scope < scope) diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h index 7b219c6854..b81bae3830 100644 --- a/src/network/networkd-link.h +++ b/src/network/networkd-link.h @@ -83,7 +83,7 @@ struct Link { unsigned link_messages; unsigned enslaving; - LIST_HEAD(Address, addresses); + Set *addresses; sd_dhcp_client *dhcp_client; sd_dhcp_lease *dhcp_lease; @@ -120,8 +120,8 @@ int link_get(Manager *m, int ifindex, Link **ret); int link_add(Manager *manager, sd_netlink_message *message, Link **ret); void link_drop(Link *link); -int link_address_drop_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata); -int link_route_drop_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata); +int link_address_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata); +int link_route_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata); void link_enter_failed(Link *link); int link_initialized(Link *link, struct udev_device *device); @@ -129,7 +129,6 @@ int link_initialized(Link *link, struct udev_device *device); void link_client_handler(Link *link); int link_update(Link *link, sd_netlink_message *message); -int link_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message, void *userdata); int link_save(Link *link); diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c index b4259cafef..07e47b668c 100644 --- a/src/network/networkd-manager.c +++ b/src/network/networkd-manager.c @@ -277,6 +277,166 @@ static int manager_connect_udev(Manager *m) { return 0; } +int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message, void *userdata) { + Manager *m = userdata; + Link *link = NULL; + uint16_t type; + unsigned char flags; + int family; + unsigned char prefixlen; + unsigned char scope; + union in_addr_union in_addr; + struct ifa_cacheinfo cinfo; + Address *address = NULL; + char buf[INET6_ADDRSTRLEN], valid_buf[FORMAT_TIMESPAN_MAX]; + const char *valid_str = NULL; + int r, ifindex; + + assert(rtnl); + assert(message); + assert(m); + + if (sd_netlink_message_is_error(message)) { + r = sd_netlink_message_get_errno(message); + if (r < 0) + log_warning_errno(r, "rtnl: failed to receive address: %m"); + + return 0; + } + + r = sd_netlink_message_get_type(message, &type); + if (r < 0) { + log_warning_errno(r, "rtnl: could not get message type: %m"); + return 0; + } else if (type != RTM_NEWADDR && type != RTM_DELADDR) { + log_warning("rtnl: received unexpected message type when processing address"); + return 0; + } + + r = sd_rtnl_message_addr_get_ifindex(message, &ifindex); + if (r < 0) { + log_warning_errno(r, "rtnl: could not get ifindex from address: %m"); + return 0; + } else if (ifindex <= 0) { + log_warning("rtnl: received address message with invalid ifindex: %d", ifindex); + return 0; + } else { + r = link_get(m, ifindex, &link); + if (r < 0 || !link) { + /* when enumerating we might be out of sync, but we will + * get the address again, so just ignore it */ + if (!m->enumerating) + log_warning("rtnl: received address for nonexistent link (%d), ignoring", ifindex); + return 0; + } + } + + r = sd_rtnl_message_addr_get_family(message, &family); + if (r < 0 || !IN_SET(family, AF_INET, AF_INET6)) { + log_link_warning(link, "rtnl: received address with invalid family, ignoring."); + return 0; + } + + r = sd_rtnl_message_addr_get_prefixlen(message, &prefixlen); + if (r < 0) { + log_link_warning_errno(link, r, "rtnl: received address with invalid prefixlen, ignoring: %m"); + return 0; + } + + r = sd_rtnl_message_addr_get_scope(message, &scope); + if (r < 0) { + log_link_warning_errno(link, r, "rtnl: received address with invalid scope, ignoring: %m"); + return 0; + } + + r = sd_rtnl_message_addr_get_flags(message, &flags); + if (r < 0) { + log_link_warning_errno(link, r, "rtnl: received address with invalid flags, ignoring: %m"); + return 0; + } + + switch (family) { + case AF_INET: + r = sd_netlink_message_read_in_addr(message, IFA_LOCAL, &in_addr.in); + if (r < 0) { + log_link_warning_errno(link, r, "rtnl: received address without valid address, ignoring: %m"); + return 0; + } + + break; + + case AF_INET6: + r = sd_netlink_message_read_in6_addr(message, IFA_ADDRESS, &in_addr.in6); + if (r < 0) { + log_link_warning_errno(link, r, "rtnl: received address without valid address, ignoring: %m"); + return 0; + } + + break; + + default: + assert_not_reached("invalid address family"); + } + + if (!inet_ntop(family, &in_addr, buf, INET6_ADDRSTRLEN)) { + log_link_warning(link, "Could not print address"); + return 0; + } + + 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 + valid_str = format_timespan(valid_buf, FORMAT_TIMESPAN_MAX, + cinfo.ifa_valid * USEC_PER_SEC, + USEC_PER_SEC); + } + + address_get(link, family, &in_addr, prefixlen, &address); + + switch (type) { + case RTM_NEWADDR: + if (address) { + log_link_debug(link, "Updating address: %s/%u (valid for %s)", buf, prefixlen, valid_str); + + address->scope = scope; + address->flags = flags; + address->cinfo = cinfo; + + } else { + r = address_add(link, family, &in_addr, prefixlen, &address); + if (r < 0) { + 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); + + address->scope = scope; + address->flags = flags; + address->cinfo = cinfo; + + link_save(link); + } + + break; + + case RTM_DELADDR: + + if (address) { + log_link_debug(link, "Removing address: %s/%u (valid for %s)", buf, prefixlen, valid_str); + address_drop(address); + } else + log_link_warning(link, "Removing non-existent address: %s/%u (valid for %s)", buf, prefixlen, valid_str); + + break; + default: + assert_not_reached("Received invalid RTNL message type"); + } + + return 1; +} + static int manager_rtnl_process_link(sd_netlink *rtnl, sd_netlink_message *message, void *userdata) { Manager *m = userdata; Link *link = NULL; @@ -410,11 +570,11 @@ static int manager_connect_rtnl(Manager *m) { if (r < 0) return r; - r = sd_netlink_add_match(m->rtnl, RTM_NEWADDR, &link_rtnl_process_address, m); + r = sd_netlink_add_match(m->rtnl, RTM_NEWADDR, &manager_rtnl_process_address, m); if (r < 0) return r; - r = sd_netlink_add_match(m->rtnl, RTM_DELADDR, &link_rtnl_process_address, m); + r = sd_netlink_add_match(m->rtnl, RTM_DELADDR, &manager_rtnl_process_address, m); if (r < 0) return r; @@ -477,14 +637,6 @@ void manager_free(Manager *m) { free(m->state_file); - sd_event_source_unref(m->udev_event_source); - udev_monitor_unref(m->udev_monitor); - udev_unref(m->udev); - - sd_bus_unref(m->bus); - sd_bus_slot_unref(m->prepare_for_sleep_slot); - sd_event_source_unref(m->bus_retry_event_source); - while ((link = hashmap_first(m->links))) link_unref(link); hashmap_free(m->links); @@ -504,6 +656,14 @@ void manager_free(Manager *m) { sd_netlink_unref(m->rtnl); sd_event_unref(m->event); + sd_event_source_unref(m->udev_event_source); + udev_monitor_unref(m->udev_monitor); + udev_unref(m->udev); + + sd_bus_unref(m->bus); + sd_bus_slot_unref(m->prepare_for_sleep_slot); + sd_event_source_unref(m->bus_retry_event_source); + free(m); } @@ -633,7 +793,7 @@ int manager_rtnl_enumerate_addresses(Manager *m) { m->enumerating = true; - k = link_rtnl_process_address(m->rtnl, addr, m); + k = manager_rtnl_process_address(m->rtnl, addr, m); if (k < 0) r = k; diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index 5d22598fc0..bdee7f1923 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -326,12 +326,12 @@ int network_get(Manager *manager, struct udev_device *device, (void) safe_atou8(attr, &name_assign_type); if (name_assign_type == NET_NAME_ENUM) - log_warning("%-*s: found matching network '%s', based on potentially unpredictable ifname", - IFNAMSIZ, ifname, network->filename); + log_warning("%s: found matching network '%s', based on potentially unpredictable ifname", + ifname, network->filename); else - log_debug("%-*s: found matching network '%s'", IFNAMSIZ, ifname, network->filename); + log_debug("%s: found matching network '%s'", ifname, network->filename); } else - log_debug("%-*s: found matching network '%s'", IFNAMSIZ, ifname, network->filename); + log_debug("%s: found matching network '%s'", ifname, network->filename); *ret = network; return 0; diff --git a/src/network/networkd-route.c b/src/network/networkd-route.c index ee1ddd81fe..1c8302ffaa 100644 --- a/src/network/networkd-route.c +++ b/src/network/networkd-route.c @@ -26,8 +26,26 @@ #include "networkd.h" #include "networkd-route.h" +int route_new(Route **ret, unsigned char rtm_protocol) { + _cleanup_route_free_ Route *route = NULL; + + route = new0(Route, 1); + if (!route) + return -ENOMEM; + + route->family = AF_UNSPEC; + route->scope = RT_SCOPE_UNIVERSE; + route->protocol = rtm_protocol; + + *ret = route; + route = NULL; + + return 0; +} + int route_new_static(Network *network, unsigned section, Route **ret) { _cleanup_route_free_ Route *route = NULL; + int r; if (section) { route = hashmap_get(network->routes_by_section, @@ -40,13 +58,9 @@ int route_new_static(Network *network, unsigned section, Route **ret) { } } - route = new0(Route, 1); - if (!route) - return -ENOMEM; - - route->family = AF_UNSPEC; - route->scope = RT_SCOPE_UNIVERSE; - route->protocol = RTPROT_STATIC; + r = route_new(&route, RTPROT_STATIC); + if (r < 0) + return r; route->network = network; @@ -64,23 +78,6 @@ int route_new_static(Network *network, unsigned section, Route **ret) { return 0; } -int route_new_dynamic(Route **ret, unsigned char rtm_protocol) { - _cleanup_route_free_ Route *route = NULL; - - route = new0(Route, 1); - if (!route) - return -ENOMEM; - - route->family = AF_UNSPEC; - route->scope = RT_SCOPE_UNIVERSE; - route->protocol = rtm_protocol; - - *ret = route; - route = NULL; - - return 0; -} - void route_free(Route *route) { if (!route) return; @@ -96,7 +93,7 @@ void route_free(Route *route) { free(route); } -int route_drop(Route *route, Link *link, +int route_remove(Route *route, Link *link, sd_netlink_message_handler_t callback) { _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL; int r; diff --git a/src/network/networkd-route.h b/src/network/networkd-route.h index 11e94d44fb..e3ed1be866 100644 --- a/src/network/networkd-route.h +++ b/src/network/networkd-route.h @@ -46,10 +46,10 @@ struct Route { }; int route_new_static(Network *network, unsigned section, Route **ret); -int route_new_dynamic(Route **ret, unsigned char rtm_protocol); +int route_new(Route **ret, unsigned char rtm_protocol); void route_free(Route *route); int route_configure(Route *route, Link *link, sd_netlink_message_handler_t callback); -int route_drop(Route *route, Link *link, sd_netlink_message_handler_t callback); +int route_remove(Route *route, Link *link, sd_netlink_message_handler_t callback); DEFINE_TRIVIAL_CLEANUP_FUNC(Route*, route_free); #define _cleanup_route_free_ _cleanup_(route_freep) diff --git a/src/network/networkd.h b/src/network/networkd.h index eea57ac158..cbec6d5b7e 100644 --- a/src/network/networkd.h +++ b/src/network/networkd.h @@ -80,6 +80,8 @@ bool manager_should_reload(Manager *m); int manager_rtnl_enumerate_links(Manager *m); int manager_rtnl_enumerate_addresses(Manager *m); +int manager_rtnl_process_address(sd_netlink *nl, sd_netlink_message *message, void *userdata); + int manager_send_changed(Manager *m, const char *property, ...) _sentinel_; int manager_save(Manager *m); diff --git a/src/network/test-network.c b/src/network/test-network.c index 5909cc790e..bac1d6781d 100644 --- a/src/network/test-network.c +++ b/src/network/test-network.c @@ -143,8 +143,8 @@ static void test_network_get(Manager *manager, struct udev_device *loopback) { static void test_address_equality(void) { _cleanup_address_free_ Address *a1 = NULL, *a2 = NULL; - assert_se(address_new_dynamic(&a1) >= 0); - assert_se(address_new_dynamic(&a2) >= 0); + assert_se(address_new(&a1) >= 0); + assert_se(address_new(&a2) >= 0); assert_se(address_equal(NULL, NULL)); assert_se(!address_equal(a1, NULL)); @@ -158,17 +158,18 @@ static void test_address_equality(void) { assert_se(address_equal(a1, a2)); assert_se(inet_pton(AF_INET, "192.168.3.9", &a1->in_addr.in)); - assert_se(address_equal(a1, a2)); + assert_se(!address_equal(a1, a2)); assert_se(inet_pton(AF_INET, "192.168.3.9", &a2->in_addr.in)); assert_se(address_equal(a1, a2)); + assert_se(inet_pton(AF_INET, "192.168.3.10", &a1->in_addr_peer.in)); + assert_se(address_equal(a1, a2)); + assert_se(inet_pton(AF_INET, "192.168.3.11", &a2->in_addr_peer.in)); + assert_se(address_equal(a1, a2)); a1->prefixlen = 10; assert_se(!address_equal(a1, a2)); a2->prefixlen = 10; assert_se(address_equal(a1, a2)); - assert_se(inet_pton(AF_INET, "192.168.3.10", &a2->in_addr.in)); - assert_se(address_equal(a1, a2)); - a1->family = AF_INET6; assert_se(!address_equal(a1, a2)); diff --git a/src/nspawn/nspawn-mount.c b/src/nspawn/nspawn-mount.c index 3d302ef9ad..6c8b1d7a26 100644 --- a/src/nspawn/nspawn-mount.c +++ b/src/nspawn/nspawn-mount.c @@ -710,13 +710,15 @@ static int mount_unified_cgroups(const char *dest) { assert(dest); - p = strjoina(dest, "/sys/fs/cgroup"); + p = prefix_roota(dest, "/sys/fs/cgroup"); + + (void) mkdir_p(p, 0755); r = path_is_mount_point(p, AT_SYMLINK_FOLLOW); if (r < 0) return log_error_errno(r, "Failed to determine if %s is mounted already: %m", p); if (r > 0) { - p = strjoina(dest, "/sys/fs/cgroup/cgroup.procs"); + p = prefix_roota(dest, "/sys/fs/cgroup/cgroup.procs"); if (access(p, F_OK) >= 0) return 0; if (errno != ENOENT) diff --git a/src/rfkill/rfkill.c b/src/rfkill/rfkill.c index d8a2f3694e..72c9eb4446 100644 --- a/src/rfkill/rfkill.c +++ b/src/rfkill/rfkill.c @@ -40,8 +40,8 @@ static const char* const rfkill_type_table[NUM_RFKILL_TYPES] = { [RFKILL_TYPE_WIMAX] = "wimax", [RFKILL_TYPE_WWAN] = "wwan", [RFKILL_TYPE_GPS] = "gps", - [RFKILL_TYPE_FM] "fm", - [RFKILL_TYPE_NFC] "nfc", + [RFKILL_TYPE_FM] = "fm", + [RFKILL_TYPE_NFC] = "nfc", }; DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(rfkill_type, int); diff --git a/src/shared/bus-util.c b/src/shared/bus-util.c index 10df3fc3d6..a5d6edbba9 100644 --- a/src/shared/bus-util.c +++ b/src/shared/bus-util.c @@ -1426,7 +1426,8 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen "CPUAccounting", "MemoryAccounting", "BlockIOAccounting", "TasksAccounting", "SendSIGHUP", "SendSIGKILL", "WakeSystem", "DefaultDependencies", "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "RemainAfterExit", - "PrivateTmp", "PrivateDevices", "PrivateNetwork", "NoNewPrivileges")) { + "PrivateTmp", "PrivateDevices", "PrivateNetwork", "NoNewPrivileges", + "SyslogLevelPrefix")) { r = parse_boolean(eq); if (r < 0) { @@ -1493,10 +1494,32 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen "UtmpIdentifier", "UtmpMode", "PAMName", "TTYPath", "StandardInput", "StandardOutput", "StandardError", "Description", "Slice", "Type", "WorkingDirectory", - "RootDirectory")) + "RootDirectory", "SyslogIdentifier")) r = sd_bus_message_append(m, "v", "s", eq); - else if (streq(field, "DeviceAllow")) { + else if (streq(field, "SyslogLevel")) { + int level; + + level = log_level_from_string(eq); + if (level < 0) { + log_error("Failed to parse %s value %s.", field, eq); + return -EINVAL; + } + + r = sd_bus_message_append(m, "v", "i", level); + + } else if (streq(field, "SyslogFacility")) { + int facility; + + facility = log_facility_unshifted_from_string(eq); + if (facility < 0) { + log_error("Failed to parse %s value %s.", field, eq); + return -EINVAL; + } + + r = sd_bus_message_append(m, "v", "i", facility); + + } else if (streq(field, "DeviceAllow")) { if (isempty(eq)) r = sd_bus_message_append(m, "v", "a(ss)", 0); @@ -1632,7 +1655,16 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen } r = sd_bus_message_append(m, "v", "t", u); + } else if (streq(field, "TimerSlackNSec")) { + nsec_t n; + r = parse_nsec(eq, &n); + if (r < 0) { + log_error("Failed to parse %s value %s", field, eq); + return -EINVAL; + } + + r = sd_bus_message_append(m, "v", "t", n); } else { log_error("Unknown assignment %s.", assignment); return -EINVAL; @@ -2137,3 +2169,42 @@ bool is_kdbus_available(void) { return ioctl(fd, KDBUS_CMD_BUS_MAKE, &cmd) >= 0; } + +int bus_property_get_rlimit( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + struct rlimit *rl; + uint64_t u; + rlim_t x; + + assert(bus); + assert(reply); + assert(userdata); + + rl = *(struct rlimit**) userdata; + if (rl) + x = rl->rlim_max; + else { + struct rlimit buf = {}; + int z; + + z = rlimit_from_string(strstr(property, "Limit")); + assert(z >= 0); + + getrlimit(z, &buf); + x = buf.rlim_max; + } + + /* rlim_t might have different sizes, let's map + * RLIMIT_INFINITY to (uint64_t) -1, so that it is the same on + * all archs */ + u = x == RLIM_INFINITY ? (uint64_t) -1 : (uint64_t) x; + + return sd_bus_message_append(reply, "t", u); +} diff --git a/src/shared/bus-util.h b/src/shared/bus-util.h index f03f951dc7..fd70842b9e 100644 --- a/src/shared/bus-util.h +++ b/src/shared/bus-util.h @@ -200,3 +200,5 @@ int bus_path_decode_unique(const char *path, const char *prefix, char **ret_send bool is_kdbus_wanted(void); bool is_kdbus_available(void); + +int bus_property_get_rlimit(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error); diff --git a/src/shared/fstab-util.c b/src/shared/fstab-util.c index db2146f8c1..c065adcfdf 100644 --- a/src/shared/fstab-util.c +++ b/src/shared/fstab-util.c @@ -25,7 +25,6 @@ #include "util.h" bool fstab_is_mount_point(const char *mount) { - _cleanup_free_ char *device = NULL; _cleanup_endmntent_ FILE *f = NULL; struct mntent *m; diff --git a/src/systemd/sd-ipv4ll.h b/src/systemd/sd-ipv4ll.h index 677505f0c6..cc85140acd 100644 --- a/src/systemd/sd-ipv4ll.h +++ b/src/systemd/sd-ipv4ll.h @@ -43,6 +43,7 @@ int sd_ipv4ll_get_address(sd_ipv4ll *ll, struct in_addr *address); int sd_ipv4ll_set_callback(sd_ipv4ll *ll, sd_ipv4ll_cb_t cb, void *userdata); int sd_ipv4ll_set_mac(sd_ipv4ll *ll, const struct ether_addr *addr); int sd_ipv4ll_set_index(sd_ipv4ll *ll, int interface_index); +int sd_ipv4ll_set_address(sd_ipv4ll *ll, const struct in_addr *address); int sd_ipv4ll_set_address_seed(sd_ipv4ll *ll, unsigned seed); bool sd_ipv4ll_is_running(sd_ipv4ll *ll); int sd_ipv4ll_start(sd_ipv4ll *ll); diff --git a/src/systemd/sd-netlink.h b/src/systemd/sd-netlink.h index e09b8c8e2d..8e1b06ee9a 100644 --- a/src/systemd/sd-netlink.h +++ b/src/systemd/sd-netlink.h @@ -137,6 +137,10 @@ int sd_rtnl_message_route_set_dst_prefixlen(sd_netlink_message *m, unsigned char 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_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); +int sd_rtnl_message_route_get_tos(sd_netlink_message *m, unsigned char *tos); +int sd_rtnl_message_route_get_table(sd_netlink_message *m, unsigned char *table); int sd_rtnl_message_route_get_dst_prefixlen(sd_netlink_message *m, unsigned char *dst_len); int sd_rtnl_message_route_get_src_prefixlen(sd_netlink_message *m, unsigned char *src_len); diff --git a/src/test/test-calendarspec.c b/src/test/test-calendarspec.c index 87e1da1258..ad36ef19b0 100644 --- a/src/test/test-calendarspec.c +++ b/src/test/test-calendarspec.c @@ -50,6 +50,44 @@ static void test_one(const char *input, const char *output) { assert_se(streq(q, p)); } +static void test_next(const char *input, const char *new_tz, usec_t after, usec_t expect) { + CalendarSpec *c; + usec_t u; + char *old_tz; + char buf[FORMAT_TIMESTAMP_MAX]; + int r; + + old_tz = getenv("TZ"); + if (old_tz) + old_tz = strdupa(old_tz); + + if (new_tz) + assert_se(setenv("TZ", new_tz, 1) >= 0); + else + assert_se(unsetenv("TZ") >= 0); + tzset(); + + assert_se(calendar_spec_from_string(input, &c) >= 0); + + printf("\"%s\"\n", input); + + u = after; + r = calendar_spec_next_usec(c, after, &u); + printf("At: %s\n", r < 0 ? strerror(-r) : format_timestamp(buf, sizeof(buf), u)); + if (expect != (usec_t)-1) + assert_se(r >= 0 && u == expect); + else + assert(r == -ENOENT); + + calendar_spec_free(c); + + if (old_tz) + assert_se(setenv("TZ", old_tz, 1) >= 0); + else + assert_se(unsetenv("TZ") >= 0); + tzset(); +} + int main(int argc, char* argv[]) { CalendarSpec *c; @@ -82,6 +120,15 @@ int main(int argc, char* argv[]) { test_one("semi-annually", "*-01,07-01 00:00:00"); 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_next("2016-03-27 03:17:00", "", 12345, 1459048620000000); + test_next("2016-03-27 03:17:00", "CET", 12345, 1459041420000000); + test_next("2016-03-27 03:17:00", "EET", 12345, -1); + test_next("2016-03-27 03:17:00 UTC", NULL, 12345, 1459048620000000); + 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); assert_se(calendar_spec_from_string("test", &c) < 0); assert_se(calendar_spec_from_string("", &c) < 0); diff --git a/src/test/test-date.c b/src/test/test-date.c index 00b569080c..bd1b2781df 100644 --- a/src/test/test-date.c +++ b/src/test/test-date.c @@ -23,12 +23,12 @@ #include "util.h" -static void test_one(const char *p) { +static void test_should_pass(const char *p) { usec_t t, q; char buf[FORMAT_TIMESTAMP_MAX], buf_relative[FORMAT_TIMESTAMP_RELATIVE_MAX]; assert_se(parse_timestamp(p, &t) >= 0); - format_timestamp(buf, sizeof(buf), t); + format_timestamp_us(buf, sizeof(buf), t); log_info("%s", buf); /* Chop off timezone */ @@ -42,23 +42,50 @@ static void test_one(const char *p) { assert_se(parse_timestamp(buf, &q) >= 0); } +static void test_should_fail(const char *p) { + usec_t t; + + assert_se(parse_timestamp(p, &t) < 0); +} + +static void test_one(const char *p) { + _cleanup_free_ char *with_utc; + + log_info("Test: %s", p); + with_utc = strjoin(p, " UTC", NULL); + test_should_pass(p); + test_should_pass(with_utc); +} + +static void test_one_noutc(const char *p) { + _cleanup_free_ char *with_utc; + + log_info("Test: %s", p); + with_utc = strjoin(p, " UTC", NULL); + test_should_pass(p); + test_should_fail(with_utc); +} + int main(int argc, char *argv[]) { test_one("17:41"); test_one("18:42:44"); + test_one("18:42:44.0"); + test_one("18:42:44.999999999999"); test_one("12-10-02 12:13:14"); test_one("12-10-2 12:13:14"); test_one("12-10-03 12:13"); test_one("2012-12-30 18:42"); test_one("2012-10-02"); test_one("Tue 2012-10-02"); - test_one("now"); + test_one_noutc("now"); test_one("yesterday"); test_one("today"); test_one("tomorrow"); - test_one("+2d"); - test_one("+2y 4d"); - test_one("5months ago"); - test_one("@1395716396"); + test_one_noutc("+2d"); + test_one_noutc("+2y 4d"); + test_one_noutc("5months ago"); + test_one_noutc("@1395716396"); + test_one_noutc("today UTC"); return 0; } diff --git a/src/tty-ask-password-agent/tty-ask-password-agent.c b/src/tty-ask-password-agent/tty-ask-password-agent.c index 93cce186f0..53986babae 100644 --- a/src/tty-ask-password-agent/tty-ask-password-agent.c +++ b/src/tty-ask-password-agent/tty-ask-password-agent.c @@ -381,7 +381,7 @@ static int wall_tty_block(void) { fd = open(p, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY); if (fd < 0) - return log_error_errno(errno, "Failed to open %s: %m", p); + return log_debug_errno(errno, "Failed to open %s: %m", p); return fd; } diff --git a/src/udev/.gitignore b/src/udev/.gitignore index ba112ce218..f5d8be3dc1 100644 --- a/src/udev/.gitignore +++ b/src/udev/.gitignore @@ -1,5 +1,4 @@ /udev.pc /keyboard-keys-from-name.gperf /keyboard-keys-from-name.h -/keyboard-keys-to-name.h /keyboard-keys-list.txt |