diff --git a/man/systemctl.xml b/man/systemctl.xml index 0778c38ca4b..f514ab64dd6 100644 --- a/man/systemctl.xml +++ b/man/systemctl.xml @@ -1245,7 +1245,7 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err enabled - Enabled via .wants/, .requires/ or alias symlinks (permanently in /etc/systemd/system/, or transiently in /run/systemd/system/). + Enabled via .wants/, .requires/ or Alias= symlinks (permanently in /etc/systemd/system/, or transiently in /run/systemd/system/). 0 @@ -1274,7 +1274,7 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err indirect - The unit file itself is not enabled, but it has a non-empty Also= setting in the [Install] unit file section, listing other unit files that might be enabled. + The unit file itself is not enabled, but it has a non-empty Also= setting in the [Install] unit file section, listing other unit files that might be enabled, or it has an alias under a different name through a symlink that is not specified in Also=. For template unit file, an instance different than the one specified in DefaultInstance= is enabled. 0 diff --git a/man/timedatectl.xml b/man/timedatectl.xml index d8a83c8add0..0dacd059581 100644 --- a/man/timedatectl.xml +++ b/man/timedatectl.xml @@ -104,13 +104,10 @@ status Show current settings of the system clock and - RTC, including whether network time synchronization is - on. Note that whether network time synchronization is on - simply reflects whether the - systemd-timesyncd.service unit is - enabled. Even if this command shows the status as off, a - different service might still synchronize the clock with the - network. + RTC, including whether network time synchronization through + systemd-timesyncd.service is active. + Even if is off, a different service might still synchronize the + clock with the network. @@ -206,13 +203,13 @@ Examples Show current settings: $ timedatectl - Local time: Di 2015-04-07 16:26:56 CEST - Universal time: Di 2015-04-07 14:26:56 UTC - RTC time: Di 2015-04-07 14:26:56 - Time zone: Europe/Berlin (CEST, +0200) - Network time on: yes -NTP synchronized: yes - RTC in local TZ: no + Local time: Thu 2017-09-21 16:08:56 CEST + Universal time: Thu 2017-09-21 14:08:56 UTC + RTC time: Thu 2017-09-21 14:08:56 + Time zone: Europe/Warsaw (CEST, +0200) + System clock synchronized: yes +systemd-timesyncd.service active: yes + RTC in local TZ: no Enable network time synchronization: diff --git a/src/shared/install.c b/src/shared/install.c index 9e4e0eaf6fd..e14a869321e 100644 --- a/src/shared/install.c +++ b/src/shared/install.c @@ -83,6 +83,20 @@ typedef struct { size_t n_rules; } Presets; +static inline bool unit_file_install_info_has_rules(UnitFileInstallInfo *i) { + assert(i); + + return !strv_isempty(i->aliases) || + !strv_isempty(i->wanted_by) || + !strv_isempty(i->required_by); +} + +static inline bool unit_file_install_info_has_also(UnitFileInstallInfo *i) { + assert(i); + + return !strv_isempty(i->also); +} + static inline void presets_freep(Presets *p) { size_t i; @@ -685,9 +699,35 @@ static int remove_marked_symlinks( return r; } +static bool is_symlink_with_known_name(const UnitFileInstallInfo *i, const char *name) { + int r; + + if (streq(name, i->name)) + return true; + + if (strv_contains(i->aliases, name)) + return true; + + /* Look for template symlink matching DefaultInstance */ + if (i->default_instance && unit_name_is_valid(i->name, UNIT_NAME_TEMPLATE)) { + _cleanup_free_ char *s = NULL; + + r = unit_name_replace_instance(i->name, i->default_instance, &s); + if (r < 0) { + if (r != -EINVAL) + return r; + + } else if (streq(name, s)) + return true; + } + + return false; +} + static int find_symlinks_fd( const char *root_dir, - const char *name, + UnitFileInstallInfo *i, + bool match_aliases, int fd, const char *path, const char *config_path, @@ -697,7 +737,7 @@ static int find_symlinks_fd( struct dirent *de; int r = 0; - assert(name); + assert(i); assert(fd >= 0); assert(path); assert(config_path); @@ -734,7 +774,8 @@ static int find_symlinks_fd( } /* This will close nfd, regardless whether it succeeds or not */ - q = find_symlinks_fd(root_dir, name, nfd, p, config_path, same_name_link); + q = find_symlinks_fd(root_dir, i, match_aliases, nfd, + p, config_path, same_name_link); if (q > 0) return 1; if (r == 0) @@ -774,24 +815,24 @@ static int find_symlinks_fd( /* Check if the symlink itself matches what we * are looking for */ - if (path_is_absolute(name)) - found_path = path_equal(p, name); + if (path_is_absolute(i->name)) + found_path = path_equal(p, i->name); else - found_path = streq(de->d_name, name); + found_path = streq(de->d_name, i->name); /* Check if what the symlink points to * matches what we are looking for */ - if (path_is_absolute(name)) - found_dest = path_equal(dest, name); + if (path_is_absolute(i->name)) + found_dest = path_equal(dest, i->name); else - found_dest = streq(basename(dest), name); + found_dest = streq(basename(dest), i->name); if (found_path && found_dest) { _cleanup_free_ char *t = NULL; /* Filter out same name links in the main * config path */ - t = path_make_absolute(name, config_path); + t = path_make_absolute(i->name, config_path); if (!t) return -ENOMEM; @@ -800,8 +841,18 @@ static int find_symlinks_fd( if (b) *same_name_link = true; - else if (found_path || found_dest) - return 1; + else if (found_path || found_dest) { + if (!match_aliases) + return 1; + + /* Check if symlink name is in the set of names used by [Install] */ + q = is_symlink_with_known_name(i, de->d_name); + log_info("is_symlink_with_known_name(%s, %s) → %d", i->name, de->d_name, q); + if (q < 0) + return q; + if (q > 0) + return 1; + } } } @@ -810,13 +861,14 @@ static int find_symlinks_fd( static int find_symlinks( const char *root_dir, - const char *name, + UnitFileInstallInfo *i, + bool match_name, const char *config_path, bool *same_name_link) { int fd; - assert(name); + assert(i); assert(config_path); assert(same_name_link); @@ -828,12 +880,15 @@ static int find_symlinks( } /* This takes possession of fd and closes it */ - return find_symlinks_fd(root_dir, name, fd, config_path, config_path, same_name_link); + return find_symlinks_fd(root_dir, i, match_name, fd, + config_path, config_path, same_name_link); } static int find_symlinks_in_scope( + UnitFileScope scope, const LookupPaths *paths, - const char *name, + UnitFileInstallInfo *i, + bool match_name, UnitFileState *state) { bool same_name_link_runtime = false, same_name_link_config = false; @@ -842,12 +897,12 @@ static int find_symlinks_in_scope( int r; assert(paths); - assert(name); + assert(i); STRV_FOREACH(p, paths->search_path) { bool same_name_link = false; - r = find_symlinks(paths->root_dir, name, *p, &same_name_link); + r = find_symlinks(paths->root_dir, i, match_name, *p, &same_name_link); if (r < 0) return r; if (r > 0) { @@ -862,6 +917,12 @@ static int find_symlinks_in_scope( return 1; } + /* look for globally enablement of user units */ + if (scope == UNIT_FILE_USER && path_is_user_config_dir(*p)) { + *state = UNIT_FILE_ENABLED; + return 1; + } + r = path_is_runtime(paths, *p, false); if (r < 0) return r; @@ -896,7 +957,7 @@ static int find_symlinks_in_scope( * outside of runtime and configuration directory, then we consider it statically enabled. Note we do that only * for instance, not for regular names, as those are merely aliases, while instances explicitly instantiate * something, and hence are a much stronger concept. */ - if (enabled_at_all && unit_name_is_valid(name, UNIT_NAME_INSTANCE)) { + if (enabled_at_all && unit_name_is_valid(i->name, UNIT_NAME_INSTANCE)) { *state = UNIT_FILE_STATIC; return 1; } @@ -2589,13 +2650,26 @@ static int unit_file_lookup_state( break; } - r = find_symlinks_in_scope(paths, i->name, &state); + /* Check if any of the Alias= symlinks have been created. + * We ignore other aliases, and only check those that would + * be created by systemctl enable for this unit. */ + r = find_symlinks_in_scope(scope, paths, i, true, &state); if (r < 0) return r; - if (r == 0) { - if (UNIT_FILE_INSTALL_INFO_HAS_RULES(i)) + if (r > 0) + break; + + /* Check if the file is known under other names. If it is, + * it might be in use. Report that as UNIT_FILE_INDIRECT. */ + r = find_symlinks_in_scope(scope, paths, i, false, &state); + if (r < 0) + return r; + if (r > 0) + state = UNIT_FILE_INDIRECT; + else { + if (unit_file_install_info_has_rules(i)) state = UNIT_FILE_DISABLED; - else if (UNIT_FILE_INSTALL_INFO_HAS_ALSO(i)) + else if (unit_file_install_info_has_also(i)) state = UNIT_FILE_INDIRECT; else state = UNIT_FILE_STATIC; diff --git a/src/shared/install.h b/src/shared/install.h index 7a5859e7295..c1fcbe96ed1 100644 --- a/src/shared/install.h +++ b/src/shared/install.h @@ -132,20 +132,6 @@ struct UnitFileInstallInfo { bool auxiliary; }; -static inline bool UNIT_FILE_INSTALL_INFO_HAS_RULES(UnitFileInstallInfo *i) { - assert(i); - - return !strv_isempty(i->aliases) || - !strv_isempty(i->wanted_by) || - !strv_isempty(i->required_by); -} - -static inline bool UNIT_FILE_INSTALL_INFO_HAS_ALSO(UnitFileInstallInfo *i) { - assert(i); - - return !strv_isempty(i->also); -} - bool unit_type_may_alias(UnitType type) _const_; bool unit_type_may_template(UnitType type) _const_; diff --git a/src/shared/path-lookup.c b/src/shared/path-lookup.c index a289511be51..bf10acda944 100644 --- a/src/shared/path-lookup.c +++ b/src/shared/path-lookup.c @@ -115,6 +115,21 @@ static int user_data_dir(char **ret, const char *suffix) { return 1; } +static const char* const user_data_unit_paths[] = { + "/usr/local/lib/systemd/user", + "/usr/local/share/systemd/user", + USER_DATA_UNIT_PATH, + "/usr/lib/systemd/user", + "/usr/share/systemd/user", + NULL +}; + +static const char* const user_config_unit_paths[] = { + USER_CONFIG_UNIT_PATH, + "/etc/systemd/user", + NULL +}; + static char** user_dirs( const char *persistent_config, const char *runtime_config, @@ -125,21 +140,6 @@ static char** user_dirs( const char *persistent_control, const char *runtime_control) { - const char * const config_unit_paths[] = { - USER_CONFIG_UNIT_PATH, - "/etc/systemd/user", - NULL - }; - - const char * const data_unit_paths[] = { - "/usr/local/lib/systemd/user", - "/usr/local/share/systemd/user", - USER_DATA_UNIT_PATH, - "/usr/lib/systemd/user", - "/usr/share/systemd/user", - NULL - }; - _cleanup_strv_free_ char **config_dirs = NULL, **data_dirs = NULL; _cleanup_free_ char *data_home = NULL; _cleanup_strv_free_ char **res = NULL; @@ -196,7 +196,7 @@ static char** user_dirs( if (strv_extend(&res, persistent_config) < 0) return NULL; - if (strv_extend_strv(&res, (char**) config_unit_paths, false) < 0) + if (strv_extend_strv(&res, (char**) user_config_unit_paths, false) < 0) return NULL; if (strv_extend(&res, runtime_config) < 0) @@ -211,7 +211,7 @@ static char** user_dirs( if (strv_extend_strv_concat(&res, data_dirs, "/systemd/user") < 0) return NULL; - if (strv_extend_strv(&res, (char**) data_unit_paths, false) < 0) + if (strv_extend_strv(&res, (char**) user_data_unit_paths, false) < 0) return NULL; if (strv_extend(&res, generator_late) < 0) @@ -226,6 +226,18 @@ static char** user_dirs( return tmp; } +bool path_is_user_data_dir(const char *path) { + assert(path); + + return strv_contains((char**) user_data_unit_paths, path); +} + +bool path_is_user_config_dir(const char *path) { + assert(path); + + return strv_contains((char**) user_config_unit_paths, path); +} + static int acquire_generator_dirs( UnitFileScope scope, const char *tempdir, diff --git a/src/shared/path-lookup.h b/src/shared/path-lookup.h index 45fba64d2ab..fc8b8ed8c78 100644 --- a/src/shared/path-lookup.h +++ b/src/shared/path-lookup.h @@ -67,6 +67,8 @@ struct LookupPaths { }; int lookup_paths_init(LookupPaths *p, UnitFileScope scope, LookupPathsFlags flags, const char *root_dir); +bool path_is_user_data_dir(const char *path); +bool path_is_user_config_dir(const char *path); int lookup_paths_reduce(LookupPaths *p); diff --git a/src/test/test-install-root.c b/src/test/test-install-root.c index 575401cb91b..e2754a07736 100644 --- a/src/test/test-install-root.c +++ b/src/test/test-install-root.c @@ -381,6 +381,8 @@ static void test_template_enable(const char *root) { UnitFileState state; const char *p; + log_info("== %s ==", __func__); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@.service", &state) == -ENOENT); assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@def.service", &state) == -ENOENT); assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@foo.service", &state) == -ENOENT); @@ -402,6 +404,8 @@ static void test_template_enable(const char *root) { assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + log_info("== %s with template@.service enabled ==", __func__); + assert_se(unit_file_enable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("template@.service"), &changes, &n_changes) >= 0); assert_se(n_changes == 1); assert_se(changes[0].type == UNIT_FILE_SYMLINK); @@ -432,6 +436,8 @@ static void test_template_enable(const char *root) { assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + log_info("== %s with template@foo.service enabled ==", __func__); + assert_se(unit_file_enable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("template@foo.service"), &changes, &n_changes) >= 0); assert_se(changes[0].type == UNIT_FILE_SYMLINK); assert_se(streq(changes[0].source, "/usr/lib/systemd/system/template@.service")); @@ -440,7 +446,7 @@ static void test_template_enable(const char *root) { unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@.service", &state) >= 0 && state == UNIT_FILE_INDIRECT); assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@foo.service", &state) >= 0 && state == UNIT_FILE_ENABLED); assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_ENABLED); @@ -463,6 +469,8 @@ static void test_template_enable(const char *root) { assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED); assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@quux.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + log_info("== %s with template-symlink@quux.service enabled ==", __func__); + assert_se(unit_file_enable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("template-symlink@quux.service"), &changes, &n_changes) >= 0); assert_se(changes[0].type == UNIT_FILE_SYMLINK); assert_se(streq(changes[0].source, "/usr/lib/systemd/system/template@.service")); @@ -471,11 +479,11 @@ static void test_template_enable(const char *root) { unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@.service", &state) >= 0 && state == UNIT_FILE_INDIRECT); assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED); assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@quux.service", &state) >= 0 && state == UNIT_FILE_ENABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@.service", &state) >= 0 && state == UNIT_FILE_INDIRECT); assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED); assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@quux.service", &state) >= 0 && state == UNIT_FILE_ENABLED); diff --git a/src/timedate/timedatectl.c b/src/timedate/timedatectl.c index 281b1534a39..a30e783c09d 100644 --- a/src/timedate/timedatectl.c +++ b/src/timedate/timedatectl.c @@ -103,13 +103,13 @@ static void print_status_info(const StatusInfo *i) { if (have_time) { xstrftime(a, "%a %Y-%m-%d %H:%M:%S %Z", localtime_r(&sec, &tm)); - printf(" Local time: %.*s\n", (int) sizeof(a), a); + printf(" Local time: %.*s\n", (int) sizeof(a), a); xstrftime(a, "%a %Y-%m-%d %H:%M:%S UTC", gmtime_r(&sec, &tm)); - printf(" Universal time: %.*s\n", (int) sizeof(a), a); + printf(" Universal time: %.*s\n", (int) sizeof(a), a); } else { - printf(" Local time: %s\n", "n/a"); - printf(" Universal time: %s\n", "n/a"); + printf(" Local time: %s\n", "n/a"); + printf(" Universal time: %s\n", "n/a"); } if (i->rtc_time > 0) { @@ -117,9 +117,9 @@ static void print_status_info(const StatusInfo *i) { rtc_sec = (time_t) (i->rtc_time / USEC_PER_SEC); xstrftime(a, "%a %Y-%m-%d %H:%M:%S", gmtime_r(&rtc_sec, &tm)); - printf(" RTC time: %.*s\n", (int) sizeof(a), a); + printf(" RTC time: %.*s\n", (int) sizeof(a), a); } else - printf(" RTC time: %s\n", "n/a"); + printf(" RTC time: %s\n", "n/a"); if (have_time) xstrftime(a, "%Z, %z", localtime_r(&sec, &tm)); @@ -134,10 +134,10 @@ static void print_status_info(const StatusInfo *i) { else tzset(); - printf(" Time zone: %s (%.*s)\n" - " Network time on: %s\n" - "NTP synchronized: %s\n" - " RTC in local TZ: %s\n", + printf(" Time zone: %s (%.*s)\n" + " System clock synchronized: %s\n" + "systemd-timesyncd.service active: %s\n" + " RTC in local TZ: %s\n", strna(i->timezone), (int) sizeof(a), have_time ? a : "n/a", i->ntp_capable ? yes_no(i->ntp_enabled) : "n/a", yes_no(i->ntp_synced),