diff --git a/man/hostname.xml b/man/hostname.xml index 5d3d46d8ce..9688450e1c 100644 --- a/man/hostname.xml +++ b/man/hostname.xml @@ -57,11 +57,12 @@ name of the local system that is set during boot using the sethostname2 system call. It should contain a single newline-terminated - hostname string. The hostname may be a free-form string up to 64 - characters in length; however, it is recommended that it consists - only of 7-bit ASCII lower-case characters and no spaces or dots, - and limits itself to the format allowed for DNS domain name - labels, even though this is not a strict requirement. + hostname string. Comments (lines starting with a `#') are ignored. + The hostname may be a free-form string up to 64 characters in length; + however, it is recommended that it consists only of 7-bit ASCII lower-case + characters and no spaces or dots, and limits itself to the format allowed + for DNS domain name labels, even though this is not a strict + requirement. Depending on the operating system, other configuration files might be checked for configuration of the hostname as well, diff --git a/src/core/hostname-setup.c b/src/core/hostname-setup.c index 217f201d05..932ddbf95a 100644 --- a/src/core/hostname-setup.c +++ b/src/core/hostname-setup.c @@ -30,35 +30,13 @@ #include "hostname-util.h" #include "hostname-setup.h" -static int read_and_strip_hostname(const char *path, char **hn) { - char *s; - int r; - - assert(path); - assert(hn); - - r = read_one_line_file(path, &s); - if (r < 0) - return r; - - hostname_cleanup(s, false); - - if (isempty(s)) { - free(s); - return -ENOENT; - } - - *hn = s; - return 0; -} - int hostname_setup(void) { int r; _cleanup_free_ char *b = NULL; const char *hn; bool enoent = false; - r = read_and_strip_hostname("/etc/hostname", &b); + r = read_hostname_config("/etc/hostname", &b); if (r < 0) { if (r == -ENOENT) enoent = true; diff --git a/src/hostname/hostnamed.c b/src/hostname/hostnamed.c index ab9ddc706a..7ff3a4e224 100644 --- a/src/hostname/hostnamed.c +++ b/src/hostname/hostnamed.c @@ -96,7 +96,7 @@ static int context_read_data(Context *c) { if (!c->data[PROP_HOSTNAME]) return -ENOMEM; - r = read_one_line_file("/etc/hostname", &c->data[PROP_STATIC_HOSTNAME]); + r = read_hostname_config("/etc/hostname", &c->data[PROP_STATIC_HOSTNAME]); if (r < 0 && r != -ENOENT) return r; diff --git a/src/shared/hostname-util.c b/src/shared/hostname-util.c index 2998fdf2c7..e336f269fa 100644 --- a/src/shared/hostname-util.c +++ b/src/shared/hostname-util.c @@ -158,3 +158,36 @@ int sethostname_idempotent(const char *s) { return 1; } + +int read_hostname_config(const char *path, char **hostname) { + _cleanup_fclose_ FILE *f = NULL; + char l[LINE_MAX]; + char *name = NULL; + + assert(path); + assert(hostname); + + f = fopen(path, "re"); + if (!f) + return -errno; + + /* may have comments, ignore them */ + FOREACH_LINE(l, f, return -errno) { + truncate_nl(l); + if (l[0] != '\0' && l[0] != '#') { + /* found line with value */ + name = hostname_cleanup(l, false); + name = strdup(name); + if (!name) + return -ENOMEM; + break; + } + } + + if (!name) + /* no non-empty line found */ + return -ENOENT; + + *hostname = name; + return 0; +} diff --git a/src/shared/hostname-util.h b/src/shared/hostname-util.h index f2821c3078..0c4763cf5a 100644 --- a/src/shared/hostname-util.h +++ b/src/shared/hostname-util.h @@ -35,3 +35,5 @@ char* hostname_cleanup(char *s, bool lowercase); bool is_localhost(const char *hostname); int sethostname_idempotent(const char *s); + +int read_hostname_config(const char *path, char **hostname); diff --git a/src/test/test-util.c b/src/test/test-util.c index 9af3e757e3..36773c109d 100644 --- a/src/test/test-util.c +++ b/src/test/test-util.c @@ -549,6 +549,52 @@ static void test_hostname_is_valid(void) { assert_se(!hostname_is_valid("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")); } +static void test_read_hostname_config(void) { + char path[] = "/tmp/hostname.XXXXXX"; + char *hostname; + int fd; + + fd = mkostemp_safe(path, O_RDWR|O_CLOEXEC); + assert(fd > 0); + close(fd); + + /* simple hostname */ + write_string_file(path, "foo"); + assert_se(read_hostname_config(path, &hostname) == 0); + assert_se(streq(hostname, "foo")); + free(hostname); + + /* with comment */ + write_string_file(path, "# comment\nfoo"); + assert_se(read_hostname_config(path, &hostname) == 0); + assert_se(streq(hostname, "foo")); + free(hostname); + + /* with comment and extra whitespace */ + write_string_file(path, "# comment\n\n foo "); + assert_se(read_hostname_config(path, &hostname) == 0); + assert_se(streq(hostname, "foo")); + free(hostname); + + /* cleans up name */ + write_string_file(path, "!foo/bar.com"); + assert_se(read_hostname_config(path, &hostname) == 0); + assert_se(streq(hostname, "foobar.com")); + free(hostname); + + /* no value set */ + hostname = (char*) 0x1234; + write_string_file(path, "# nothing here\n"); + assert_se(read_hostname_config(path, &hostname) == -ENOENT); + assert_se(hostname == (char*) 0x1234); /* does not touch argument on error */ + + /* nonexisting file */ + assert_se(read_hostname_config("/non/existing", &hostname) == -ENOENT); + assert_se(hostname == (char*) 0x1234); /* does not touch argument on error */ + + unlink(path); +} + static void test_u64log2(void) { assert_se(u64log2(0) == 0); assert_se(u64log2(8) == 3); @@ -1481,6 +1527,7 @@ int main(int argc, char *argv[]) { test_foreach_word_quoted(); test_memdup_multiply(); test_hostname_is_valid(); + test_read_hostname_config(); test_u64log2(); test_protect_errno(); test_parse_size();