• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

systemd / systemd / 13823965644

12 Mar 2025 10:54PM UTC coverage: 71.864% (-0.03%) from 71.892%
13823965644

push

github

web-flow
Fix several issues found by Coverity (#36726)

263 of 301 new or added lines in 31 files covered. (87.38%)

836 existing lines in 49 files now uncovered.

295777 of 411581 relevant lines covered (71.86%)

718837.67 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

74.43
/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 "sd-daemon.h"
10

11
#include "alloc-util.h"
12
#include "creds-util.h"
13
#include "fd-util.h"
14
#include "fileio.h"
15
#include "fs-util.h"
16
#include "hexdecoct.h"
17
#include "hostname-setup.h"
18
#include "hostname-util.h"
19
#include "initrd-util.h"
20
#include "log.h"
21
#include "macro.h"
22
#include "proc-cmdline.h"
23
#include "siphash24.h"
24
#include "string-table.h"
25
#include "string-util.h"
26

27
static int sethostname_idempotent_full(const char *s, bool really) {
332✔
28
        struct utsname u;
332✔
29

30
        assert(s);
332✔
31

32
        if (uname(&u) < 0)
332✔
33
                return -errno;
×
34

35
        if (streq_ptr(s, u.nodename))
332✔
36
                return 0;
37

38
        if (really &&
460✔
39
            sethostname(s, strlen(s)) < 0)
230✔
40
                return -errno;
×
41

42
        return 1;
43
}
44

45
int sethostname_idempotent(const char *s) {
246✔
46
        return sethostname_idempotent_full(s, true);
246✔
47
}
48

49
int shorten_overlong(const char *s, char **ret) {
76✔
50
        _cleanup_free_ char *h = NULL;
76✔
51

52
        /* Shorten an overlong name to HOST_NAME_MAX or to the first dot,
53
         * whatever comes earlier. */
54

55
        assert(s);
76✔
56
        assert(ret);
76✔
57

58
        h = strdup(s);
76✔
59
        if (!h)
76✔
60
                return -ENOMEM;
61

62
        if (hostname_is_valid(h, 0)) {
76✔
63
                *ret = TAKE_PTR(h);
73✔
64
                return 0;
73✔
65
        }
66

67
        char *p = strchr(h, '.');
3✔
68
        if (p)
3✔
69
                *p = 0;
2✔
70

71
        strshorten(h, HOST_NAME_MAX);
3✔
72

73
        if (!hostname_is_valid(h, /* flags= */ 0))
3✔
74
                return -EDOM;
75

76
        *ret = TAKE_PTR(h);
2✔
77
        return 1;
2✔
78
}
79

80
static int acquire_hostname_from_credential(char **ret) {
×
81
        _cleanup_free_ char *cred = NULL;
×
82
        int r;
×
83

84
        assert(ret);
×
85

86
        r = read_credential_with_decryption("system.hostname", (void **) &cred, /* ret_size= */ NULL);
×
87
        if (r < 0)
×
88
                return log_warning_errno(r, "Failed to read system.hostname credential, ignoring: %m");
×
89
        if (r == 0) /* not found */
×
90
                return -ENXIO;
91

92
        if (!hostname_is_valid(cred, VALID_HOSTNAME_TRAILING_DOT)) /* check that the hostname we return is valid */
×
93
                return log_warning_errno(SYNTHETIC_ERRNO(EBADMSG), "Hostname specified in system.hostname credential is invalid, ignoring: %s", cred);
×
94

95
        log_info("Initializing hostname from credential.");
×
96
        *ret = TAKE_PTR(cred);
×
97
        return 0;
×
98
}
99

100
int read_etc_hostname_stream(FILE *f, bool substitute_wildcards, char **ret) {
64✔
101
        int r;
64✔
102

103
        assert(f);
64✔
104
        assert(ret);
64✔
105

106
        for (;;) {
72✔
107
                _cleanup_free_ char *line = NULL;
68✔
108

109
                r = read_stripped_line(f, LONG_LINE_MAX, &line);
68✔
110
                if (r < 0)
68✔
111
                        return r;
112
                if (r == 0) /* EOF without any hostname? the file is empty, let's treat that exactly like no file at all: ENOENT */
68✔
113
                        return -ENOENT;
114

115
                /* File may have empty lines or comments, ignore them */
116
                if (IN_SET(line[0], '\0', '#'))
12✔
117
                        continue;
4✔
118

119
                if (substitute_wildcards) {
8✔
120
                        r = hostname_substitute_wildcards(line);
1✔
121
                        if (r < 0)
1✔
122
                                return r;
123
                }
124

125
                hostname_cleanup(line); /* normalize the hostname */
8✔
126

127
                /* check that the hostname we return is valid */
128
                if (!hostname_is_valid(
15✔
129
                                    line,
130
                                    VALID_HOSTNAME_TRAILING_DOT|
131
                                    (substitute_wildcards ? 0 : VALID_HOSTNAME_QUESTION_MARK)))
132
                        return -EBADMSG;
133

134
                *ret = TAKE_PTR(line);
8✔
135
                return 0;
8✔
136
        }
137
}
138

139
int read_etc_hostname(const char *path, bool substitute_wildcards, char **ret) {
197✔
140
        _cleanup_fclose_ FILE *f = NULL;
197✔
141

142
        assert(ret);
197✔
143

144
        if (!path)
197✔
145
                path = "/etc/hostname";
189✔
146

147
        f = fopen(path, "re");
197✔
148
        if (!f)
197✔
149
                return -errno;
188✔
150

151
        return read_etc_hostname_stream(f, substitute_wildcards, ret);
9✔
152
}
153

154
void hostname_update_source_hint(const char *hostname, HostnameSource source) {
154✔
155
        int r;
154✔
156

157
        assert(hostname);
154✔
158

159
        /* Why save the value and not just create a flag file? This way we will
160
         * notice if somebody sets the hostname directly (not going through hostnamed).
161
         */
162

163
        if (source == HOSTNAME_DEFAULT) {
154✔
164
                r = write_string_file("/run/systemd/default-hostname", hostname,
32✔
165
                                      WRITE_STRING_FILE_CREATE | WRITE_STRING_FILE_ATOMIC);
166
                if (r < 0)
32✔
167
                        log_warning_errno(r, "Failed to create \"/run/systemd/default-hostname\", ignoring: %m");
×
168
        } else
169
                (void) unlink_or_warn("/run/systemd/default-hostname");
122✔
170
}
154✔
171

172
int hostname_setup(bool really) {
86✔
173
        _cleanup_free_ char *hn = NULL;
86✔
174
        HostnameSource source;
86✔
175
        bool enoent = false;
86✔
176
        int r;
86✔
177

178
        r = proc_cmdline_get_key("systemd.hostname", 0, &hn);
86✔
179
        if (r < 0)
86✔
180
                log_warning_errno(r, "Failed to retrieve system hostname from kernel command line, ignoring: %m");
×
181
        else if (r > 0) {
86✔
182
                if (hostname_is_valid(hn, VALID_HOSTNAME_TRAILING_DOT))
86✔
183
                        source = HOSTNAME_TRANSIENT;
184
                else  {
185
                        log_warning("Hostname specified on kernel command line is invalid, ignoring: %s", hn);
×
186
                        hn = mfree(hn);
×
187
                }
188
        }
189

190
        if (!hn) {
86✔
191
                r = read_etc_hostname(/* path= */ NULL, /* substitute_wildcards= */ true, &hn);
×
192
                if (r == -ENOENT)
×
193
                        enoent = true;
194
                else if (r < 0)
×
195
                        log_warning_errno(r, "Failed to read configured hostname, ignoring: %m");
×
196
                else
197
                        source = HOSTNAME_STATIC;
198
        }
199

200
        if (!hn) {
86✔
201
                r = acquire_hostname_from_credential(&hn);
×
202
                if (r >= 0)
×
203
                        source = HOSTNAME_TRANSIENT;
×
204
        }
205

206
        if (!hn) {
86✔
207
                /* Don't override the hostname if it is already set and not explicitly configured */
208

209
                r = gethostname_full(GET_HOSTNAME_ALLOW_LOCALHOST, &hn);
×
210
                if (r == -ENOMEM)
×
211
                        return log_oom();
×
212
                if (r >= 0) {
×
213
                        log_debug("No hostname configured, leaving existing hostname <%s> in place.", hn);
×
214
                        goto finish;
×
215
                }
216

217
                if (enoent)
×
218
                        log_info("No hostname configured, using default hostname.");
×
219

220
                hn = get_default_hostname();
×
221
                if (!hn)
×
222
                        return log_oom();
×
223

224
                source = HOSTNAME_DEFAULT;
225
        }
226

227
        r = sethostname_idempotent_full(hn, really);
86✔
228
        if (r < 0)
86✔
229
                return log_warning_errno(r, "Failed to set hostname to <%s>: %m", hn);
×
230
        if (r == 0)
86✔
231
                log_debug("Hostname was already set to <%s>.", hn);
7✔
232
        else
233
                log_info("Hostname %s to <%s>.",
79✔
234
                         really ? "set" : "would have been set",
235
                         hn);
236

237
        if (really)
86✔
238
                hostname_update_source_hint(hn, source);
85✔
239

240
finish:
1✔
241
        if (!in_initrd())
86✔
242
                (void) sd_notifyf(/* unset_environment= */ false, "X_SYSTEMD_HOSTNAME=%s", hn);
25✔
243

244
        return 0;
245
}
246

247
static const char* const hostname_source_table[] = {
248
        [HOSTNAME_STATIC]    = "static",
249
        [HOSTNAME_TRANSIENT] = "transient",
250
        [HOSTNAME_DEFAULT]   = "default",
251
};
252

253
DEFINE_STRING_TABLE_LOOKUP(hostname_source, HostnameSource);
148✔
254

255
int hostname_substitute_wildcards(char *name) {
197✔
256
        static const sd_id128_t key = SD_ID128_MAKE(98,10,ad,df,8d,7d,4f,b5,89,1b,4b,56,ac,c2,26,8f);
197✔
257
        sd_id128_t mid = SD_ID128_NULL;
197✔
258
        size_t left_bits = 0, counter = 0;
197✔
259
        uint64_t h = 0;
197✔
260
        int r;
197✔
261

262
        assert(name);
197✔
263

264
        /* Replaces every occurrence of '?' in the specified string with a nibble hashed from
265
         * /etc/machine-id. This is supposed to be used on /etc/hostname files that want to automatically
266
         * configure a hostname derived from the machine ID in some form.
267
         *
268
         * Note that this does not directly use the machine ID, because that's not necessarily supposed to be
269
         * public information to be broadcast on the network, while the hostname certainly is. */
270

271
        for (char *n = name; ; n++) {
23✔
272
                n = strchr(n, '?');
243✔
273
                if (!n)
220✔
274
                        return 0;
197✔
275

276
                if (left_bits <= 0) {
23✔
277
                        if (sd_id128_is_null(mid)) {
3✔
278
                                r = sd_id128_get_machine(&mid);
2✔
279
                                if (r < 0)
2✔
UNCOV
280
                                        return r;
×
281
                        }
282

283
                        struct siphash state;
3✔
284
                        siphash24_init(&state, key.bytes);
3✔
285
                        siphash24_compress(&mid, sizeof(mid), &state);
3✔
286
                        siphash24_compress(&counter, sizeof(counter), &state); /* counter mode */
3✔
287
                        h = siphash24_finalize(&state);
3✔
288
                        left_bits = sizeof(h) * 8;
3✔
289
                        counter++;
3✔
290
                }
291

292
                assert(left_bits >= 4);
23✔
293
                *n = hexchar(h & 0xf);
23✔
294
                h >>= 4;
23✔
295
                left_bits -= 4;
23✔
296
        }
297
}
298

299
char* get_default_hostname(void) {
43✔
300
        int r;
43✔
301

302
        _cleanup_free_ char *h = get_default_hostname_raw();
86✔
303
        if (!h)
43✔
304
                return NULL;
305

306
        r = hostname_substitute_wildcards(h);
43✔
307
        if (r < 0) {
43✔
UNCOV
308
                log_debug_errno(r, "Failed to substitute wildcards in hostname, falling back to built-in name: %m");
×
309
                return strdup(FALLBACK_HOSTNAME);
×
310
        }
311

312
        return TAKE_PTR(h);
313
}
314

315
int gethostname_full(GetHostnameFlags flags, char **ret) {
2,374✔
316
        _cleanup_free_ char *buf = NULL, *fallback = NULL;
2,374✔
317
        struct utsname u;
2,374✔
318
        const char *s;
2,374✔
319

320
        assert(ret);
2,374✔
321

322
        assert_se(uname(&u) >= 0);
2,374✔
323

324
        s = u.nodename;
2,374✔
325
        if (isempty(s) || streq(s, "(none)") ||
2,374✔
326
            (!FLAGS_SET(flags, GET_HOSTNAME_ALLOW_LOCALHOST) && is_localhost(s)) ||
2,374✔
327
            (FLAGS_SET(flags, GET_HOSTNAME_SHORT) && s[0] == '.')) {
2,374✔
UNCOV
328
                if (!FLAGS_SET(flags, GET_HOSTNAME_FALLBACK_DEFAULT))
×
329
                        return -ENXIO;
330

UNCOV
331
                s = fallback = get_default_hostname();
×
332
                if (!s)
×
333
                        return -ENOMEM;
334

UNCOV
335
                if (FLAGS_SET(flags, GET_HOSTNAME_SHORT) && s[0] == '.')
×
336
                        return -ENXIO;
337
        }
338

339
        if (FLAGS_SET(flags, GET_HOSTNAME_SHORT))
8✔
340
                buf = strdupcspn(s, ".");
8✔
341
        else
342
                buf = strdup(s);
2,366✔
343
        if (!buf)
2,374✔
344
                return -ENOMEM;
345

346
        *ret = TAKE_PTR(buf);
2,374✔
347
        return 0;
2,374✔
348
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc