]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/hostname-setup.c
Merge pull request #18745 from keszybz/stop-using-fstrings
[thirdparty/systemd.git] / src / shared / hostname-setup.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <errno.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <sys/utsname.h>
7 #include <unistd.h>
8
9 #include "alloc-util.h"
10 #include "fd-util.h"
11 #include "fileio.h"
12 #include "fs-util.h"
13 #include "hostname-setup.h"
14 #include "hostname-util.h"
15 #include "log.h"
16 #include "macro.h"
17 #include "proc-cmdline.h"
18 #include "string-table.h"
19 #include "string-util.h"
20 #include "util.h"
21
22 static int sethostname_idempotent_full(const char *s, bool really) {
23 char buf[HOST_NAME_MAX + 1] = {};
24
25 assert(s);
26
27 if (gethostname(buf, sizeof(buf) - 1) < 0)
28 return -errno;
29
30 if (streq(buf, s))
31 return 0;
32
33 if (really &&
34 sethostname(s, strlen(s)) < 0)
35 return -errno;
36
37 return 1;
38 }
39
40 int sethostname_idempotent(const char *s) {
41 return sethostname_idempotent_full(s, true);
42 }
43
44 bool get_hostname_filtered(char ret[static HOST_NAME_MAX + 1]) {
45 char buf[HOST_NAME_MAX + 1] = {};
46
47 /* Returns true if we got a good hostname, false otherwise. */
48
49 if (gethostname(buf, sizeof(buf) - 1) < 0)
50 return false; /* This can realistically only fail with ENAMETOOLONG.
51 * Let's treat that case the same as an invalid hostname. */
52
53 if (isempty(buf))
54 return false;
55
56 /* This is the built-in kernel default hostname */
57 if (streq(buf, "(none)"))
58 return false;
59
60 memcpy(ret, buf, sizeof buf);
61 return true;
62 }
63
64 int shorten_overlong(const char *s, char **ret) {
65 char *h, *p;
66
67 /* Shorten an overlong name to HOST_NAME_MAX or to the first dot,
68 * whatever comes earlier. */
69
70 assert(s);
71
72 h = strdup(s);
73 if (!h)
74 return -ENOMEM;
75
76 if (hostname_is_valid(h, 0)) {
77 *ret = h;
78 return 0;
79 }
80
81 p = strchr(h, '.');
82 if (p)
83 *p = 0;
84
85 strshorten(h, HOST_NAME_MAX);
86
87 if (!hostname_is_valid(h, 0)) {
88 free(h);
89 return -EDOM;
90 }
91
92 *ret = h;
93 return 1;
94 }
95
96 int read_etc_hostname_stream(FILE *f, char **ret) {
97 int r;
98
99 assert(f);
100 assert(ret);
101
102 for (;;) {
103 _cleanup_free_ char *line = NULL;
104 char *p;
105
106 r = read_line(f, LONG_LINE_MAX, &line);
107 if (r < 0)
108 return r;
109 if (r == 0) /* EOF without any hostname? the file is empty, let's treat that exactly like no file at all: ENOENT */
110 return -ENOENT;
111
112 p = strstrip(line);
113
114 /* File may have empty lines or comments, ignore them */
115 if (!IN_SET(*p, '\0', '#')) {
116 char *copy;
117
118 hostname_cleanup(p); /* normalize the hostname */
119
120 if (!hostname_is_valid(p, VALID_HOSTNAME_TRAILING_DOT)) /* check that the hostname we return is valid */
121 return -EBADMSG;
122
123 copy = strdup(p);
124 if (!copy)
125 return -ENOMEM;
126
127 *ret = copy;
128 return 0;
129 }
130 }
131 }
132
133 int read_etc_hostname(const char *path, char **ret) {
134 _cleanup_fclose_ FILE *f = NULL;
135
136 assert(ret);
137
138 if (!path)
139 path = "/etc/hostname";
140
141 f = fopen(path, "re");
142 if (!f)
143 return -errno;
144
145 return read_etc_hostname_stream(f, ret);
146 }
147
148 void hostname_update_source_hint(const char *hostname, HostnameSource source) {
149 int r;
150
151 /* Why save the value and not just create a flag file? This way we will
152 * notice if somebody sets the hostname directly (not going through hostnamed).
153 */
154
155 if (source == HOSTNAME_DEFAULT) {
156 r = write_string_file("/run/systemd/default-hostname", hostname,
157 WRITE_STRING_FILE_CREATE | WRITE_STRING_FILE_ATOMIC);
158 if (r < 0)
159 log_warning_errno(r, "Failed to create \"/run/systemd/default-hostname\": %m");
160 } else
161 unlink_or_warn("/run/systemd/default-hostname");
162 }
163
164 int hostname_setup(bool really) {
165 _cleanup_free_ char *b = NULL;
166 const char *hn = NULL;
167 HostnameSource source;
168 bool enoent = false;
169 int r;
170
171 r = proc_cmdline_get_key("systemd.hostname", 0, &b);
172 if (r < 0)
173 log_warning_errno(r, "Failed to retrieve system hostname from kernel command line, ignoring: %m");
174 else if (r > 0) {
175 if (hostname_is_valid(b, true)) {
176 hn = b;
177 source = HOSTNAME_TRANSIENT;
178 } else {
179 log_warning("Hostname specified on kernel command line is invalid, ignoring: %s", b);
180 b = mfree(b);
181 }
182 }
183
184 if (!hn) {
185 r = read_etc_hostname(NULL, &b);
186 if (r < 0) {
187 if (r == -ENOENT)
188 enoent = true;
189 else
190 log_warning_errno(r, "Failed to read configured hostname: %m");
191 } else {
192 hn = b;
193 source = HOSTNAME_STATIC;
194 }
195 }
196
197 if (!hn) {
198 /* Don't override the hostname if it is already set and not explicitly configured */
199
200 char buf[HOST_NAME_MAX + 1] = {};
201 if (get_hostname_filtered(buf)) {
202 log_debug("No hostname configured, leaving existing hostname <%s> in place.", buf);
203 return 0;
204 }
205
206 if (enoent)
207 log_info("No hostname configured, using default hostname.");
208
209 hn = b = get_default_hostname();
210 if (!hn)
211 return log_oom();
212
213 source = HOSTNAME_DEFAULT;
214
215 }
216
217 r = sethostname_idempotent_full(hn, really);
218 if (r < 0)
219 return log_warning_errno(r, "Failed to set hostname to <%s>: %m", hn);
220 if (r == 0)
221 log_debug("Hostname was already set to <%s>.", hn);
222 else
223 log_info("Hostname %s to <%s>.",
224 really ? "set" : "would have been set",
225 hn);
226
227 if (really)
228 hostname_update_source_hint(hn, source);
229
230 return r;
231 }
232
233 static const char* const hostname_source_table[] = {
234 [HOSTNAME_STATIC] = "static",
235 [HOSTNAME_TRANSIENT] = "transient",
236 [HOSTNAME_DEFAULT] = "default",
237 };
238
239 DEFINE_STRING_TABLE_LOOKUP(hostname_source, HostnameSource);