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

systemd / systemd / 15720680019

17 Jun 2025 07:58PM UTC coverage: 72.087% (-0.02%) from 72.109%
15720680019

push

github

web-flow
sd-lldp: several improvements (#37845)

This makes
- sd-lldp-tx not send machine ID as chassis ID, but use application
specific machine ID,
- sd-lldp-tx emit vlan ID if it is running on a vlan interface,
- Describe() DBus method also reply LLDP configurations,
- io.systemd.Network.GetLLDPNeighbors varlink method provides vlan ID,
if received.

Closes #37613.

59 of 76 new or added lines in 3 files covered. (77.63%)

4663 existing lines in 75 files now uncovered.

300494 of 416851 relevant lines covered (72.09%)

704683.58 hits per line

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

92.79
/src/basic/hostname-util.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <stdlib.h>
4

5
#include "alloc-util.h"
6
#include "env-file.h"
7
#include "hostname-util.h"
8
#include "log.h"
9
#include "os-util.h"
10
#include "string-util.h"
11
#include "strv.h"
12
#include "user-util.h"
13

14
char* get_default_hostname_raw(void) {
45✔
15
        int r;
45✔
16

17
        /* Returns the default hostname, and leaves any ??? in place. */
18

19
        const char *e = secure_getenv("SYSTEMD_DEFAULT_HOSTNAME");
45✔
20
        if (e) {
45✔
UNCOV
21
                if (hostname_is_valid(e, VALID_HOSTNAME_QUESTION_MARK))
×
22
                        return strdup(e);
45✔
23

UNCOV
24
                log_debug("Invalid hostname in $SYSTEMD_DEFAULT_HOSTNAME, ignoring: %s", e);
×
25
        }
26

27
        _cleanup_free_ char *f = NULL;
45✔
28
        r = parse_os_release(NULL, "DEFAULT_HOSTNAME", &f);
45✔
29
        if (r < 0)
45✔
UNCOV
30
                log_debug_errno(r, "Failed to parse os-release, ignoring: %m");
×
31
        else if (f) {
45✔
32
                if (hostname_is_valid(f, VALID_HOSTNAME_QUESTION_MARK))
×
UNCOV
33
                        return TAKE_PTR(f);
×
34

UNCOV
35
                log_debug("Invalid hostname in os-release, ignoring: %s", f);
×
36
        }
37

38
        return strdup(FALLBACK_HOSTNAME);
45✔
39
}
40

41
bool valid_ldh_char(char c) {
77,004✔
42
        /* "LDH" → "Letters, digits, hyphens", as per RFC 5890, Section 2.3.1 */
43

44
        return ascii_isalpha(c) ||
77,004✔
45
                ascii_isdigit(c) ||
77,004✔
46
                c == '-';
47
}
48

49
bool hostname_is_valid(const char *s, ValidHostnameFlags flags) {
4,225✔
50
        unsigned n_dots = 0;
4,225✔
51
        const char *p;
4,225✔
52
        bool dot, hyphen;
4,225✔
53

54
        /* Check if s looks like a valid hostname or FQDN. This does not do full DNS validation, but only
55
         * checks if the name is composed of allowed characters and the length is not above the maximum
56
         * allowed by Linux (c.f. dns_name_is_valid()). A trailing dot is allowed if
57
         * VALID_HOSTNAME_TRAILING_DOT flag is set and at least two components are present in the name. Note
58
         * that due to the restricted charset and length this call is substantially more conservative than
59
         * dns_name_is_valid(). Doesn't accept empty hostnames, hostnames with leading dots, and hostnames
60
         * with multiple dots in a sequence. Doesn't allow hyphens at the beginning or end of label. */
61

62
        if (isempty(s))
4,225✔
63
                return false;
64

65
        if (streq(s, ".host")) /* Used by the container logic to denote the "root container" */
4,217✔
66
                return FLAGS_SET(flags, VALID_HOSTNAME_DOT_HOST);
113✔
67

68
        for (p = s, dot = hyphen = true; *p; p++)
80,649✔
69
                if (*p == '.') {
76,585✔
70
                        if (dot || hyphen)
11,768✔
71
                                return false;
72

73
                        dot = true;
11,756✔
74
                        hyphen = false;
11,756✔
75
                        n_dots++;
11,756✔
76

77
                } else if (*p == '-') {
64,817✔
78
                        if (dot)
2,253✔
79
                                return false;
80

81
                        dot = false;
82
                        hyphen = true;
83

84
                } else {
85
                        if (!valid_ldh_char(*p) && (*p != '?' || !FLAGS_SET(flags, VALID_HOSTNAME_QUESTION_MARK)))
62,564✔
86
                                return false;
87

88
                        dot = false;
89
                        hyphen = false;
90
                }
91

92
        if (dot && (n_dots < 2 || !FLAGS_SET(flags, VALID_HOSTNAME_TRAILING_DOT)))
4,064✔
93
                return false;
94
        if (hyphen)
4,058✔
95
                return false;
96

97
        if (p-s > HOST_NAME_MAX) /* Note that HOST_NAME_MAX is 64 on Linux, but DNS allows domain names up to
4,058✔
98
                                  * 255 characters */
99
                return false;
5✔
100

101
        return true;
102
}
103

104
char* hostname_cleanup(char *s) {
409✔
105
        char *p, *d;
409✔
106
        bool dot, hyphen;
409✔
107

108
        assert(s);
409✔
109

110
        for (p = s, d = s, dot = hyphen = true; *p && d - s < HOST_NAME_MAX; p++)
13,337✔
111
                if (*p == '.') {
12,928✔
112
                        if (dot || hyphen)
741✔
113
                                continue;
16✔
114

115
                        *(d++) = '.';
725✔
116
                        dot = true;
725✔
117
                        hyphen = false;
725✔
118

119
                } else if (*p == '-') {
12,187✔
120
                        if (dot)
1,459✔
121
                                continue;
4✔
122

123
                        *(d++) = '-';
1,455✔
124
                        dot = false;
1,455✔
125
                        hyphen = true;
1,455✔
126

127
                } else if (valid_ldh_char(*p) || *p == '?') {
10,728✔
128
                        *(d++) = *p;
10,704✔
129
                        dot = false;
10,704✔
130
                        hyphen = false;
10,704✔
131
                }
132

133
        if (d > s && IN_SET(d[-1], '-', '.'))
409✔
134
                /* The dot can occur at most once, but we might have multiple
135
                 * hyphens, hence the loop */
136
                d--;
7✔
137
        *d = 0;
409✔
138

139
        return s;
409✔
140
}
141

142
bool is_localhost(const char *hostname) {
142,755✔
143
        assert(hostname);
142,755✔
144

145
        /* This tries to identify local host and domain names
146
         * described in RFC6761 plus the redhatism of localdomain */
147

148
        return STRCASE_IN_SET(
142,755✔
149
                        hostname,
150
                        "localhost",
151
                        "localhost.",
152
                        "localhost.localdomain",
153
                        "localhost.localdomain.") ||
142,459✔
154
                endswith_no_case(hostname, ".localhost") ||
284,888✔
155
                endswith_no_case(hostname, ".localhost.") ||
284,828✔
156
                endswith_no_case(hostname, ".localhost.localdomain") ||
427,523✔
157
                endswith_no_case(hostname, ".localhost.localdomain.");
142,369✔
158
}
159

160
int get_pretty_hostname(char **ret) {
12✔
161
        _cleanup_free_ char *n = NULL;
12✔
162
        int r;
12✔
163

164
        assert(ret);
12✔
165

166
        r = parse_env_file(NULL, "/etc/machine-info", "PRETTY_HOSTNAME", &n);
12✔
167
        if (r < 0)
12✔
168
                return r;
169

170
        if (isempty(n))
12✔
171
                return -ENXIO;
172

173
        *ret = TAKE_PTR(n);
×
UNCOV
174
        return 0;
×
175
}
176

177
int split_user_at_host(const char *s, char **ret_user, char **ret_host) {
239✔
178
        _cleanup_free_ char *u = NULL, *h = NULL;
478✔
179

180
        /* Splits a user@host expression (one of those we accept on --machine= and similar). Returns NULL in
181
         * each of the two return parameters if that part was left empty. */
182

183
        assert(s);
239✔
184

185
        const char *rhs = strchr(s, '@');
239✔
186
        if (rhs) {
239✔
187
                if (ret_user && rhs > s) {
192✔
188
                        u = strndup(s, rhs - s);
183✔
189
                        if (!u)
183✔
190
                                return -ENOMEM;
191
                }
192

193
                if (ret_host && rhs[1] != 0) {
192✔
194
                        h = strdup(rhs + 1);
145✔
195
                        if (!h)
145✔
196
                                return -ENOMEM;
197
                }
198

199
        } else {
200
                if (isempty(s))
47✔
201
                        return -EINVAL;
202

203
                if (ret_host) {
45✔
204
                        h = strdup(s);
44✔
205
                        if (!h)
44✔
206
                                return -ENOMEM;
207
                }
208
        }
209

210
        if (ret_user)
237✔
211
                *ret_user = TAKE_PTR(u);
230✔
212
        if (ret_host)
237✔
213
                *ret_host = TAKE_PTR(h);
231✔
214

215
        return !!rhs; /* return > 0 if '@' was specified, 0 otherwise */
237✔
216
}
217

218
int machine_spec_valid(const char *s) {
146✔
219
        _cleanup_free_ char *u = NULL, *h = NULL;
146✔
220
        int r;
146✔
221

222
        assert(s);
146✔
223

224
        r = split_user_at_host(s, &u, &h);
146✔
225
        if (r == -EINVAL)
146✔
226
                return false;
227
        if (r < 0)
145✔
228
                return r;
229

230
        if (u && !valid_user_group_name(u, VALID_USER_RELAX | VALID_USER_ALLOW_NUMERIC))
145✔
231
                return false;
232

233
        if (h && !hostname_is_valid(h, VALID_HOSTNAME_DOT_HOST))
145✔
234
                return false;
2✔
235

236
        return true;
237
}
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