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

systemd / systemd / 17014398938

16 Aug 2025 10:22PM UTC coverage: 72.237% (+0.06%) from 72.175%
17014398938

push

github

bluca
meson: compile nss-util.c only when at least one nss module is enabled

Follow-up for ea7075347.

302508 of 418772 relevant lines covered (72.24%)

651155.16 hits per line

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

74.01
/src/shared/hostname-setup.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <stdio.h>
4
#include <sys/utsname.h>
5
#include <unistd.h>
6

7
#include "sd-daemon.h"
8

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

24
static int sethostname_idempotent_full(const char *s, bool really) {
282✔
25
        struct utsname u;
282✔
26

27
        assert(s);
282✔
28

29
        if (uname(&u) < 0)
282✔
30
                return -errno;
×
31

32
        if (streq_ptr(s, u.nodename))
282✔
33
                return 0;
34

35
        if (really &&
334✔
36
            sethostname(s, strlen(s)) < 0)
167✔
37
                return -errno;
×
38

39
        return 1;
40
}
41

42
int sethostname_idempotent(const char *s) {
245✔
43
        return sethostname_idempotent_full(s, true);
245✔
44
}
45

46
int shorten_overlong(const char *s, char **ret) {
89✔
47
        _cleanup_free_ char *h = NULL;
89✔
48

49
        /* Shorten an overlong name to HOST_NAME_MAX or to the first dot,
50
         * whatever comes earlier. */
51

52
        assert(s);
89✔
53
        assert(ret);
89✔
54

55
        h = strdup(s);
89✔
56
        if (!h)
89✔
57
                return -ENOMEM;
58

59
        if (hostname_is_valid(h, 0)) {
89✔
60
                *ret = TAKE_PTR(h);
86✔
61
                return 0;
86✔
62
        }
63

64
        char *p = strchr(h, '.');
3✔
65
        if (p)
3✔
66
                *p = 0;
2✔
67

68
        strshorten(h, HOST_NAME_MAX);
3✔
69

70
        if (!hostname_is_valid(h, /* flags= */ 0))
3✔
71
                return -EDOM;
72

73
        *ret = TAKE_PTR(h);
2✔
74
        return 1;
2✔
75
}
76

77
static int acquire_hostname_from_credential(char **ret) {
×
78
        _cleanup_free_ char *cred = NULL;
×
79
        int r;
×
80

81
        assert(ret);
×
82

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

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

92
        log_info("Initializing hostname from credential.");
×
93
        *ret = TAKE_PTR(cred);
×
94
        return 0;
×
95
}
96

97
int read_etc_hostname_stream(FILE *f, bool substitute_wildcards, char **ret) {
64✔
98
        int r;
64✔
99

100
        assert(f);
64✔
101
        assert(ret);
64✔
102

103
        for (;;) {
72✔
104
                _cleanup_free_ char *line = NULL;
68✔
105

106
                r = read_stripped_line(f, LONG_LINE_MAX, &line);
68✔
107
                if (r < 0)
68✔
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 */
68✔
110
                        return -ENOENT;
111

112
                /* File may have empty lines or comments, ignore them */
113
                if (IN_SET(line[0], '\0', '#'))
13✔
114
                        continue;
4✔
115

116
                if (substitute_wildcards) {
9✔
117
                        r = hostname_substitute_wildcards(line);
1✔
118
                        if (r < 0)
1✔
119
                                return r;
120
                }
121

122
                hostname_cleanup(line); /* normalize the hostname */
9✔
123

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

131
                *ret = TAKE_PTR(line);
9✔
132
                return 0;
9✔
133
        }
134
}
135

136
int read_etc_hostname(const char *path, bool substitute_wildcards, char **ret) {
221✔
137
        _cleanup_fclose_ FILE *f = NULL;
221✔
138

139
        assert(ret);
221✔
140

141
        if (!path)
221✔
142
                path = etc_hostname();
213✔
143

144
        f = fopen(path, "re");
221✔
145
        if (!f)
221✔
146
                return -errno;
211✔
147

148
        return read_etc_hostname_stream(f, substitute_wildcards, ret);
10✔
149
}
150

151
void hostname_update_source_hint(const char *hostname, HostnameSource source) {
112✔
152
        int r;
112✔
153

154
        assert(hostname);
112✔
155

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

160
        if (source == HOSTNAME_DEFAULT) {
112✔
161
                r = write_string_file("/run/systemd/default-hostname", hostname,
35✔
162
                                      WRITE_STRING_FILE_CREATE | WRITE_STRING_FILE_ATOMIC);
163
                if (r < 0)
35✔
164
                        log_warning_errno(r, "Failed to create \"/run/systemd/default-hostname\", ignoring: %m");
×
165
        } else
166
                (void) unlink_or_warn("/run/systemd/default-hostname");
77✔
167
}
112✔
168

169
int hostname_setup(bool really) {
37✔
170
        _cleanup_free_ char *hn = NULL;
37✔
171
        HostnameSource source;
37✔
172
        bool enoent = false;
37✔
173
        int r;
37✔
174

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

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

197
        if (!hn) {
37✔
198
                r = acquire_hostname_from_credential(&hn);
×
199
                if (r >= 0)
×
200
                        source = HOSTNAME_TRANSIENT;
×
201
        }
202

203
        if (!hn) {
37✔
204
                /* Don't override the hostname if it is already set and not explicitly configured */
205

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

214
                if (enoent)
×
215
                        log_info("No hostname configured, using default hostname.");
×
216

217
                hn = get_default_hostname();
×
218
                if (!hn)
×
219
                        return log_oom();
×
220

221
                source = HOSTNAME_DEFAULT;
222
        }
223

224
        r = sethostname_idempotent_full(hn, really);
37✔
225
        if (r < 0)
37✔
226
                return log_warning_errno(r, "Failed to set hostname to <%s>: %m", hn);
×
227
        if (r == 0)
37✔
228
                log_debug("Hostname was already set to <%s>.", hn);
4✔
229
        else
230
                log_info("Hostname %s to <%s>.",
33✔
231
                         really ? "set" : "would have been set",
232
                         hn);
233

234
        if (really)
37✔
235
                hostname_update_source_hint(hn, source);
36✔
236

237
finish:
1✔
238
        if (!in_initrd())
37✔
239
                (void) sd_notifyf(/* unset_environment= */ false, "X_SYSTEMD_HOSTNAME=%s", hn);
26✔
240

241
        return 0;
242
}
243

244
static const char* const hostname_source_table[] = {
245
        [HOSTNAME_STATIC]    = "static",
246
        [HOSTNAME_TRANSIENT] = "transient",
247
        [HOSTNAME_DEFAULT]   = "default",
248
};
249

250
DEFINE_STRING_TABLE_LOOKUP(hostname_source, HostnameSource);
163✔
251

252
int hostname_substitute_wildcards(char *name) {
230✔
253
        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);
230✔
254
        sd_id128_t mid = SD_ID128_NULL;
230✔
255
        size_t left_bits = 0, counter = 0;
230✔
256
        uint64_t h = 0;
230✔
257
        int r;
230✔
258

259
        assert(name);
230✔
260

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

268
        for (char *n = name; ; n++) {
23✔
269
                n = strchr(n, '?');
276✔
270
                if (!n)
253✔
271
                        return 0;
230✔
272

273
                if (left_bits <= 0) {
23✔
274
                        if (sd_id128_is_null(mid)) {
3✔
275
                                r = sd_id128_get_machine(&mid);
2✔
276
                                if (r < 0)
2✔
277
                                        return r;
×
278
                        }
279

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

289
                assert(left_bits >= 4);
23✔
290
                *n = hexchar(h & 0xf);
23✔
291
                h >>= 4;
23✔
292
                left_bits -= 4;
23✔
293
        }
294
}
295

296
char* get_default_hostname(void) {
47✔
297
        int r;
47✔
298

299
        _cleanup_free_ char *h = get_default_hostname_raw();
94✔
300
        if (!h)
47✔
301
                return NULL;
302

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

309
        return TAKE_PTR(h);
310
}
311

312
int gethostname_full(GetHostnameFlags flags, char **ret) {
1,374✔
313
        _cleanup_free_ char *buf = NULL, *fallback = NULL;
1,374✔
314
        struct utsname u;
1,374✔
315
        const char *s;
1,374✔
316

317
        assert(ret);
1,374✔
318

319
        if (uname(&u) < 0)
1,374✔
320
                return -errno;
×
321

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

329
                s = fallback = get_default_hostname();
×
330
                if (!s)
×
331
                        return -ENOMEM;
332

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

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

344
        *ret = TAKE_PTR(buf);
1,374✔
345
        return 0;
1,374✔
346
}
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