]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/hostname/hostnamed.c
tree-wide: use TAKE_PTR() and TAKE_FD() macros
[thirdparty/systemd.git] / src / hostname / hostnamed.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
7640a5de
LP
2/***
3 This file is part of systemd.
4
41550d40 5 Copyright 2011 Lennart Poettering
7640a5de
LP
6
7 systemd is free software; you can redistribute it and/or modify it
5430f7f2
LP
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
7640a5de
LP
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
5430f7f2 15 Lesser General Public License for more details.
7640a5de 16
5430f7f2 17 You should have received a copy of the GNU Lesser General Public License
7640a5de
LP
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19***/
20
7640a5de
LP
21#include <errno.h>
22#include <string.h>
f426cc5d 23#include <sys/utsname.h>
cf0fbc49 24#include <unistd.h>
7640a5de 25
b5efdb8a 26#include "alloc-util.h"
6bedfcbb 27#include "bus-util.h"
ad740100 28#include "def.h"
4d1a6904 29#include "env-util.h"
6bedfcbb 30#include "fileio-label.h"
958b66ea 31#include "hostname-util.h"
6bedfcbb 32#include "parse-util.h"
bb15fafe 33#include "path-util.h"
6bedfcbb
LP
34#include "selinux-util.h"
35#include "strv.h"
ee104e11 36#include "user-util.h"
6bedfcbb
LP
37#include "util.h"
38#include "virt.h"
91f9dcaf 39
799298d6
JG
40#define VALID_DEPLOYMENT_CHARS (DIGITS LETTERS "-.:")
41
7640a5de
LP
42enum {
43 PROP_HOSTNAME,
44 PROP_STATIC_HOSTNAME,
45 PROP_PRETTY_HOSTNAME,
46 PROP_ICON_NAME,
7871c8e9 47 PROP_CHASSIS,
799298d6 48 PROP_DEPLOYMENT,
ce0f1493 49 PROP_LOCATION,
f426cc5d
DH
50 PROP_KERNEL_NAME,
51 PROP_KERNEL_RELEASE,
9be3455f 52 PROP_KERNEL_VERSION,
44c32988
DH
53 PROP_OS_PRETTY_NAME,
54 PROP_OS_CPE_NAME,
7640a5de
LP
55 _PROP_MAX
56};
57
66a4c743
LP
58typedef struct Context {
59 char *data[_PROP_MAX];
60 Hashmap *polkit_registry;
61} Context;
ad740100 62
66a4c743 63static void context_reset(Context *c) {
7640a5de
LP
64 int p;
65
66a4c743
LP
66 assert(c);
67
1f6b4113 68 for (p = 0; p < _PROP_MAX; p++)
a1e58e8e 69 c->data[p] = mfree(c->data[p]);
7640a5de
LP
70}
71
36e34057 72static void context_free(Context *c) {
66a4c743
LP
73 assert(c);
74
75 context_reset(c);
36e34057 76 bus_verify_polkit_async_registry_free(c->polkit_registry);
66a4c743
LP
77}
78
79static int context_read_data(Context *c) {
7640a5de 80 int r;
f426cc5d 81 struct utsname u;
7640a5de 82
66a4c743
LP
83 assert(c);
84
85 context_reset(c);
7640a5de 86
f426cc5d
DH
87 assert_se(uname(&u) >= 0);
88 c->data[PROP_KERNEL_NAME] = strdup(u.sysname);
89 c->data[PROP_KERNEL_RELEASE] = strdup(u.release);
9be3455f
DH
90 c->data[PROP_KERNEL_VERSION] = strdup(u.version);
91 if (!c->data[PROP_KERNEL_NAME] || !c->data[PROP_KERNEL_RELEASE] ||
92 !c->data[PROP_KERNEL_VERSION])
f426cc5d
DH
93 return -ENOMEM;
94
66a4c743
LP
95 c->data[PROP_HOSTNAME] = gethostname_malloc();
96 if (!c->data[PROP_HOSTNAME])
7640a5de
LP
97 return -ENOMEM;
98
f35cb39e 99 r = read_etc_hostname(NULL, &c->data[PROP_STATIC_HOSTNAME]);
7640a5de
LP
100 if (r < 0 && r != -ENOENT)
101 return r;
102
103 r = parse_env_file("/etc/machine-info", NEWLINE,
66a4c743
LP
104 "PRETTY_HOSTNAME", &c->data[PROP_PRETTY_HOSTNAME],
105 "ICON_NAME", &c->data[PROP_ICON_NAME],
106 "CHASSIS", &c->data[PROP_CHASSIS],
799298d6 107 "DEPLOYMENT", &c->data[PROP_DEPLOYMENT],
ce0f1493 108 "LOCATION", &c->data[PROP_LOCATION],
7640a5de
LP
109 NULL);
110 if (r < 0 && r != -ENOENT)
111 return r;
112
44c32988
DH
113 r = parse_env_file("/etc/os-release", NEWLINE,
114 "PRETTY_NAME", &c->data[PROP_OS_PRETTY_NAME],
115 "CPE_NAME", &c->data[PROP_OS_CPE_NAME],
116 NULL);
ece174c5 117 if (r == -ENOENT)
5ae4d543
LP
118 r = parse_env_file("/usr/lib/os-release", NEWLINE,
119 "PRETTY_NAME", &c->data[PROP_OS_PRETTY_NAME],
120 "CPE_NAME", &c->data[PROP_OS_CPE_NAME],
121 NULL);
5ae4d543 122
44c32988
DH
123 if (r < 0 && r != -ENOENT)
124 return r;
125
7640a5de
LP
126 return 0;
127}
128
7871c8e9 129static bool valid_chassis(const char *chassis) {
7871c8e9
LP
130 assert(chassis);
131
132 return nulstr_contains(
133 "vm\0"
134 "container\0"
135 "desktop\0"
136 "laptop\0"
34b52450 137 "convertible\0"
7871c8e9
LP
138 "server\0"
139 "tablet\0"
c49e59c1 140 "handset\0"
25fa306e
LP
141 "watch\0"
142 "embedded\0",
7871c8e9
LP
143 chassis);
144}
145
799298d6
JG
146static bool valid_deployment(const char *deployment) {
147 assert(deployment);
148
c2142cf1 149 return in_charset(deployment, VALID_DEPLOYMENT_CHARS);
799298d6
JG
150}
151
7871c8e9 152static const char* fallback_chassis(void) {
7640a5de
LP
153 char *type;
154 unsigned t;
219bfe38 155 int v, r;
7871c8e9 156
75f86906 157 v = detect_virtualization();
75f86906 158 if (VIRTUALIZATION_IS_VM(v))
7871c8e9 159 return "vm";
75f86906 160 if (VIRTUALIZATION_IS_CONTAINER(v))
7871c8e9
LP
161 return "container";
162
219bfe38 163 r = read_one_line_file("/sys/class/dmi/id/chassis_type", &type);
7871c8e9 164 if (r < 0)
219bfe38 165 goto try_acpi;
7871c8e9
LP
166
167 r = safe_atou(type, &t);
168 free(type);
169 if (r < 0)
219bfe38 170 goto try_acpi;
7871c8e9 171
219bfe38
LP
172 /* We only list the really obvious cases here. The DMI data is unreliable enough, so let's not do any
173 additional guesswork on top of that.
174
175 See the SMBIOS Specification 3.0 section 7.4.1 for details about the values listed here:
176
177 https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_3.0.0.pdf
7871c8e9
LP
178 */
179
219bfe38 180 switch (t) {
7871c8e9 181
219bfe38
LP
182 case 0x3: /* Desktop */
183 case 0x4: /* Low Profile Desktop */
184 case 0x6: /* Mini Tower */
185 case 0x7: /* Tower */
7871c8e9
LP
186 return "desktop";
187
219bfe38
LP
188 case 0x8: /* Portable */
189 case 0x9: /* Laptop */
190 case 0xA: /* Notebook */
191 case 0xE: /* Sub Notebook */
7871c8e9 192 return "laptop";
7640a5de 193
219bfe38
LP
194 case 0xB: /* Hand Held */
195 return "handset";
196
197 case 0x11: /* Main Server Chassis */
198 case 0x1C: /* Blade */
199 case 0x1D: /* Blade Enclosure */
7871c8e9 200 return "server";
7640a5de 201
219bfe38 202 case 0x1E: /* Tablet */
7871c8e9 203 return "tablet";
b70af833
DH
204
205 case 0x1F: /* Convertible */
b4227dbb 206 case 0x20: /* Detachable */
b70af833 207 return "convertible";
7871c8e9
LP
208 }
209
219bfe38
LP
210try_acpi:
211 r = read_one_line_file("/sys/firmware/acpi/pm_profile", &type);
7640a5de
LP
212 if (r < 0)
213 return NULL;
214
215 r = safe_atou(type, &t);
216 free(type);
7640a5de
LP
217 if (r < 0)
218 return NULL;
219
219bfe38
LP
220 /* We only list the really obvious cases here as the ACPI data is not really super reliable.
221 *
222 * See the ACPI 5.0 Spec Section 5.2.9.1 for details:
223 *
224 * http://www.acpi.info/DOWNLOADS/ACPIspec50.pdf
41550d40 225 */
7640a5de 226
219bfe38 227 switch(t) {
7640a5de 228
219bfe38
LP
229 case 1: /* Desktop */
230 case 3: /* Workstation */
231 case 6: /* Appliance PC */
7871c8e9 232 return "desktop";
7640a5de 233
219bfe38 234 case 2: /* Mobile */
7871c8e9
LP
235 return "laptop";
236
219bfe38
LP
237 case 4: /* Enterprise Server */
238 case 5: /* SOHO Server */
239 case 7: /* Performance Server */
7871c8e9 240 return "server";
f3f4f008 241
219bfe38 242 case 8: /* Tablet */
f3f4f008 243 return "tablet";
7640a5de
LP
244 }
245
7640a5de
LP
246 return NULL;
247}
248
66a4c743 249static char* context_fallback_icon_name(Context *c) {
7871c8e9
LP
250 const char *chassis;
251
66a4c743
LP
252 assert(c);
253
254 if (!isempty(c->data[PROP_CHASSIS]))
255 return strappend("computer-", c->data[PROP_CHASSIS]);
7871c8e9
LP
256
257 chassis = fallback_chassis();
258 if (chassis)
259 return strappend("computer-", chassis);
260
261 return strdup("computer");
262}
263
799298d6 264
c779a442 265static bool hostname_is_useful(const char *hn) {
fecc80c1 266 return !isempty(hn) && !is_localhost(hn);
c779a442
SW
267}
268
269static int context_update_kernel_hostname(Context *c) {
270 const char *static_hn;
7640a5de
LP
271 const char *hn;
272
66a4c743
LP
273 assert(c);
274
c779a442
SW
275 static_hn = c->data[PROP_STATIC_HOSTNAME];
276
277 /* /etc/hostname with something other than "localhost"
278 * has the highest preference ... */
279 if (hostname_is_useful(static_hn))
280 hn = static_hn;
281
f7340ab2 282 /* ... the transient host name, (ie: DHCP) comes next ... */
c779a442 283 else if (!isempty(c->data[PROP_HOSTNAME]))
66a4c743 284 hn = c->data[PROP_HOSTNAME];
7640a5de 285
c779a442
SW
286 /* ... fallback to static "localhost.*" ignored above ... */
287 else if (!isempty(static_hn))
288 hn = static_hn;
289
290 /* ... and the ultimate fallback */
291 else
8146c32b 292 hn = FALLBACK_HOSTNAME;
c779a442 293
605f81a8 294 if (sethostname_idempotent(hn) < 0)
7640a5de
LP
295 return -errno;
296
297 return 0;
298}
299
66a4c743 300static int context_write_data_static_hostname(Context *c) {
7640a5de 301
66a4c743
LP
302 assert(c);
303
304 if (isempty(c->data[PROP_STATIC_HOSTNAME])) {
7640a5de
LP
305
306 if (unlink("/etc/hostname") < 0)
307 return errno == ENOENT ? 0 : -errno;
308
309 return 0;
310 }
66a4c743 311 return write_string_file_atomic_label("/etc/hostname", c->data[PROP_STATIC_HOSTNAME]);
7640a5de
LP
312}
313
f200e8bb 314static int context_write_data_machine_info(Context *c) {
4f34ed54 315
7640a5de
LP
316 static const char * const name[_PROP_MAX] = {
317 [PROP_PRETTY_HOSTNAME] = "PRETTY_HOSTNAME",
7871c8e9 318 [PROP_ICON_NAME] = "ICON_NAME",
799298d6
JG
319 [PROP_CHASSIS] = "CHASSIS",
320 [PROP_DEPLOYMENT] = "DEPLOYMENT",
ce0f1493 321 [PROP_LOCATION] = "LOCATION",
7640a5de
LP
322 };
323
0ccad099 324 _cleanup_strv_free_ char **l = NULL;
7640a5de
LP
325 int r, p;
326
66a4c743
LP
327 assert(c);
328
717603e3 329 r = load_env_file(NULL, "/etc/machine-info", NULL, &l);
7640a5de
LP
330 if (r < 0 && r != -ENOENT)
331 return r;
332
ce0f1493 333 for (p = PROP_PRETTY_HOSTNAME; p <= PROP_LOCATION; p++) {
d77ab3f7
LP
334 _cleanup_free_ char *t = NULL;
335 char **u;
7640a5de
LP
336
337 assert(name[p]);
338
66a4c743 339 if (isempty(c->data[p])) {
f8440af5 340 strv_env_unset(l, name[p]);
7640a5de
LP
341 continue;
342 }
343
605405c6 344 t = strjoin(name[p], "=", c->data[p]);
d77ab3f7 345 if (!t)
7640a5de 346 return -ENOMEM;
7640a5de
LP
347
348 u = strv_env_set(l, t);
7640a5de
LP
349 if (!u)
350 return -ENOMEM;
0ccad099
LP
351
352 strv_free(l);
7640a5de
LP
353 l = u;
354 }
355
356 if (strv_isempty(l)) {
7640a5de
LP
357 if (unlink("/etc/machine-info") < 0)
358 return errno == ENOENT ? 0 : -errno;
359
360 return 0;
361 }
362
0ccad099 363 return write_env_file_label("/etc/machine-info", l);
7640a5de
LP
364}
365
66a4c743
LP
366static int property_get_icon_name(
367 sd_bus *bus,
368 const char *path,
369 const char *interface,
370 const char *property,
371 sd_bus_message *reply,
ebcf1f97
LP
372 void *userdata,
373 sd_bus_error *error) {
7640a5de 374
66a4c743
LP
375 _cleanup_free_ char *n = NULL;
376 Context *c = userdata;
377 const char *name;
7640a5de 378
66a4c743
LP
379 if (isempty(c->data[PROP_ICON_NAME]))
380 name = n = context_fallback_icon_name(c);
7640a5de 381 else
66a4c743
LP
382 name = c->data[PROP_ICON_NAME];
383
384 if (!name)
385 return -ENOMEM;
7640a5de 386
ebcf1f97 387 return sd_bus_message_append(reply, "s", name);
7640a5de
LP
388}
389
66a4c743
LP
390static int property_get_chassis(
391 sd_bus *bus,
392 const char *path,
393 const char *interface,
394 const char *property,
395 sd_bus_message *reply,
ebcf1f97
LP
396 void *userdata,
397 sd_bus_error *error) {
7871c8e9 398
66a4c743
LP
399 Context *c = userdata;
400 const char *name;
7871c8e9 401
66a4c743 402 if (isempty(c->data[PROP_CHASSIS]))
7871c8e9
LP
403 name = fallback_chassis();
404 else
66a4c743 405 name = c->data[PROP_CHASSIS];
7871c8e9 406
ebcf1f97 407 return sd_bus_message_append(reply, "s", name);
7871c8e9
LP
408}
409
19070062 410static int method_set_hostname(sd_bus_message *m, void *userdata, sd_bus_error *error) {
66a4c743
LP
411 Context *c = userdata;
412 const char *name;
102d8f81 413 int interactive;
66a4c743
LP
414 char *h;
415 int r;
d200735e 416
19070062
LP
417 assert(m);
418 assert(c);
419
66a4c743
LP
420 r = sd_bus_message_read(m, "sb", &name, &interactive);
421 if (r < 0)
ebcf1f97 422 return r;
d200735e 423
66a4c743
LP
424 if (isempty(name))
425 name = c->data[PROP_STATIC_HOSTNAME];
7640a5de 426
66a4c743 427 if (isempty(name))
8146c32b 428 name = FALLBACK_HOSTNAME;
7640a5de 429
8fb49443 430 if (!hostname_is_valid(name, false))
ebcf1f97 431 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid hostname '%s'", name);
7640a5de 432
66a4c743 433 if (streq_ptr(name, c->data[PROP_HOSTNAME]))
df2d202e 434 return sd_bus_reply_method_return(m, NULL);
7640a5de 435
c529695e
LP
436 r = bus_verify_polkit_async(
437 m,
438 CAP_SYS_ADMIN,
439 "org.freedesktop.hostname1.set-hostname",
403ed0e5 440 NULL,
c529695e
LP
441 interactive,
442 UID_INVALID,
443 &c->polkit_registry,
444 error);
66a4c743 445 if (r < 0)
ebcf1f97 446 return r;
66a4c743
LP
447 if (r == 0)
448 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
7640a5de 449
66a4c743
LP
450 h = strdup(name);
451 if (!h)
ebcf1f97 452 return -ENOMEM;
7640a5de 453
66a4c743
LP
454 free(c->data[PROP_HOSTNAME]);
455 c->data[PROP_HOSTNAME] = h;
7640a5de 456
c779a442 457 r = context_update_kernel_hostname(c);
66a4c743 458 if (r < 0) {
da927ba9 459 log_error_errno(r, "Failed to set host name: %m");
1b4cd646 460 return sd_bus_error_set_errnof(error, r, "Failed to set hostname: %m");
66a4c743 461 }
7640a5de 462
66a4c743 463 log_info("Changed host name to '%s'", strna(c->data[PROP_HOSTNAME]));
7640a5de 464
19070062 465 (void) sd_bus_emit_properties_changed(sd_bus_message_get_bus(m), "/org/freedesktop/hostname1", "org.freedesktop.hostname1", "Hostname", NULL);
7640a5de 466
df2d202e 467 return sd_bus_reply_method_return(m, NULL);
66a4c743 468}
7640a5de 469
19070062 470static int method_set_static_hostname(sd_bus_message *m, void *userdata, sd_bus_error *error) {
66a4c743
LP
471 Context *c = userdata;
472 const char *name;
102d8f81 473 int interactive;
66a4c743 474 int r;
7640a5de 475
19070062
LP
476 assert(m);
477 assert(c);
478
66a4c743
LP
479 r = sd_bus_message_read(m, "sb", &name, &interactive);
480 if (r < 0)
ebcf1f97 481 return r;
7640a5de 482
3c6f7c34 483 name = empty_to_null(name);
7640a5de 484
66a4c743 485 if (streq_ptr(name, c->data[PROP_STATIC_HOSTNAME]))
df2d202e 486 return sd_bus_reply_method_return(m, NULL);
7640a5de 487
c529695e
LP
488 r = bus_verify_polkit_async(
489 m,
490 CAP_SYS_ADMIN,
491 "org.freedesktop.hostname1.set-static-hostname",
403ed0e5 492 NULL,
c529695e
LP
493 interactive,
494 UID_INVALID,
495 &c->polkit_registry,
496 error);
66a4c743 497 if (r < 0)
ebcf1f97 498 return r;
66a4c743
LP
499 if (r == 0)
500 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
7640a5de 501
3c6f7c34 502 if (isempty(name))
a1e58e8e 503 c->data[PROP_STATIC_HOSTNAME] = mfree(c->data[PROP_STATIC_HOSTNAME]);
3c6f7c34 504 else {
66a4c743 505 char *h;
7640a5de 506
8fb49443 507 if (!hostname_is_valid(name, false))
ebcf1f97 508 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid static hostname '%s'", name);
7640a5de 509
66a4c743
LP
510 h = strdup(name);
511 if (!h)
ebcf1f97 512 return -ENOMEM;
7640a5de 513
66a4c743
LP
514 free(c->data[PROP_STATIC_HOSTNAME]);
515 c->data[PROP_STATIC_HOSTNAME] = h;
516 }
7640a5de 517
c779a442
SW
518 r = context_update_kernel_hostname(c);
519 if (r < 0) {
da927ba9 520 log_error_errno(r, "Failed to set host name: %m");
1b4cd646 521 return sd_bus_error_set_errnof(error, r, "Failed to set hostname: %m");
c779a442
SW
522 }
523
66a4c743
LP
524 r = context_write_data_static_hostname(c);
525 if (r < 0) {
da927ba9 526 log_error_errno(r, "Failed to write static host name: %m");
1b4cd646 527 return sd_bus_error_set_errnof(error, r, "Failed to set static hostname: %m");
66a4c743 528 }
7640a5de 529
66a4c743 530 log_info("Changed static host name to '%s'", strna(c->data[PROP_STATIC_HOSTNAME]));
7640a5de 531
19070062 532 (void) sd_bus_emit_properties_changed(sd_bus_message_get_bus(m), "/org/freedesktop/hostname1", "org.freedesktop.hostname1", "StaticHostname", NULL);
7640a5de 533
df2d202e 534 return sd_bus_reply_method_return(m, NULL);
66a4c743 535}
7640a5de 536
19070062 537static int set_machine_info(Context *c, sd_bus_message *m, int prop, sd_bus_message_handler_t cb, sd_bus_error *error) {
102d8f81 538 int interactive;
66a4c743
LP
539 const char *name;
540 int r;
541
542 assert(c);
66a4c743
LP
543 assert(m);
544
545 r = sd_bus_message_read(m, "sb", &name, &interactive);
546 if (r < 0)
ebcf1f97 547 return r;
66a4c743 548
3c6f7c34 549 name = empty_to_null(name);
66a4c743
LP
550
551 if (streq_ptr(name, c->data[prop]))
df2d202e 552 return sd_bus_reply_method_return(m, NULL);
7640a5de 553
66a4c743
LP
554 /* Since the pretty hostname should always be changed at the
555 * same time as the static one, use the same policy action for
556 * both... */
557
c529695e
LP
558 r = bus_verify_polkit_async(
559 m,
560 CAP_SYS_ADMIN,
561 prop == PROP_PRETTY_HOSTNAME ? "org.freedesktop.hostname1.set-static-hostname" : "org.freedesktop.hostname1.set-machine-info",
403ed0e5 562 NULL,
c529695e
LP
563 interactive,
564 UID_INVALID,
565 &c->polkit_registry,
566 error);
66a4c743 567 if (r < 0)
ebcf1f97 568 return r;
66a4c743
LP
569 if (r == 0)
570 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
571
3c6f7c34 572 if (isempty(name))
a1e58e8e 573 c->data[prop] = mfree(c->data[prop]);
3c6f7c34 574 else {
66a4c743
LP
575 char *h;
576
577 /* The icon name might ultimately be used as file
578 * name, so better be safe than sorry */
579
ae6c3cc0 580 if (prop == PROP_ICON_NAME && !filename_is_valid(name))
ebcf1f97 581 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid icon name '%s'", name);
6294aa76 582 if (prop == PROP_PRETTY_HOSTNAME && string_has_cc(name, NULL))
ebcf1f97 583 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid pretty host name '%s'", name);
66a4c743 584 if (prop == PROP_CHASSIS && !valid_chassis(name))
ebcf1f97 585 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid chassis '%s'", name);
799298d6
JG
586 if (prop == PROP_DEPLOYMENT && !valid_deployment(name))
587 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid deployment '%s'", name);
ce0f1493
LP
588 if (prop == PROP_LOCATION && string_has_cc(name, NULL))
589 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid location '%s'", name);
66a4c743
LP
590
591 h = strdup(name);
592 if (!h)
ebcf1f97 593 return -ENOMEM;
66a4c743
LP
594
595 free(c->data[prop]);
596 c->data[prop] = h;
7640a5de
LP
597 }
598
f200e8bb 599 r = context_write_data_machine_info(c);
66a4c743 600 if (r < 0) {
da927ba9 601 log_error_errno(r, "Failed to write machine info: %m");
1b4cd646 602 return sd_bus_error_set_errnof(error, r, "Failed to write machine info: %m");
66a4c743 603 }
7640a5de 604
66a4c743
LP
605 log_info("Changed %s to '%s'",
606 prop == PROP_PRETTY_HOSTNAME ? "pretty host name" :
799298d6 607 prop == PROP_DEPLOYMENT ? "deployment" :
ce0f1493 608 prop == PROP_LOCATION ? "location" :
66a4c743 609 prop == PROP_CHASSIS ? "chassis" : "icon name", strna(c->data[prop]));
7640a5de 610
19070062
LP
611 (void) sd_bus_emit_properties_changed(
612 sd_bus_message_get_bus(m),
613 "/org/freedesktop/hostname1",
614 "org.freedesktop.hostname1",
615 prop == PROP_PRETTY_HOSTNAME ? "PrettyHostname" :
616 prop == PROP_DEPLOYMENT ? "Deployment" :
617 prop == PROP_LOCATION ? "Location" :
618 prop == PROP_CHASSIS ? "Chassis" : "IconName" , NULL);
7640a5de 619
df2d202e 620 return sd_bus_reply_method_return(m, NULL);
66a4c743 621}
7640a5de 622
19070062
LP
623static int method_set_pretty_hostname(sd_bus_message *m, void *userdata, sd_bus_error *error) {
624 return set_machine_info(userdata, m, PROP_PRETTY_HOSTNAME, method_set_pretty_hostname, error);
7640a5de
LP
625}
626
19070062
LP
627static int method_set_icon_name(sd_bus_message *m, void *userdata, sd_bus_error *error) {
628 return set_machine_info(userdata, m, PROP_ICON_NAME, method_set_icon_name, error);
66a4c743
LP
629}
630
19070062
LP
631static int method_set_chassis(sd_bus_message *m, void *userdata, sd_bus_error *error) {
632 return set_machine_info(userdata, m, PROP_CHASSIS, method_set_chassis, error);
66a4c743
LP
633}
634
19070062
LP
635static int method_set_deployment(sd_bus_message *m, void *userdata, sd_bus_error *error) {
636 return set_machine_info(userdata, m, PROP_DEPLOYMENT, method_set_deployment, error);
799298d6
JG
637}
638
19070062
LP
639static int method_set_location(sd_bus_message *m, void *userdata, sd_bus_error *error) {
640 return set_machine_info(userdata, m, PROP_LOCATION, method_set_location, error);
ce0f1493
LP
641}
642
66a4c743
LP
643static const sd_bus_vtable hostname_vtable[] = {
644 SD_BUS_VTABLE_START(0),
caffbef6 645 SD_BUS_PROPERTY("Hostname", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_HOSTNAME, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
66a4c743
LP
646 SD_BUS_PROPERTY("StaticHostname", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_STATIC_HOSTNAME, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
647 SD_BUS_PROPERTY("PrettyHostname", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_PRETTY_HOSTNAME, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
648 SD_BUS_PROPERTY("IconName", "s", property_get_icon_name, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
649 SD_BUS_PROPERTY("Chassis", "s", property_get_chassis, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
ce0f1493
LP
650 SD_BUS_PROPERTY("Deployment", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_DEPLOYMENT, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
651 SD_BUS_PROPERTY("Location", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_LOCATION, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
f426cc5d
DH
652 SD_BUS_PROPERTY("KernelName", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_KERNEL_NAME, SD_BUS_VTABLE_PROPERTY_CONST),
653 SD_BUS_PROPERTY("KernelRelease", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_KERNEL_RELEASE, SD_BUS_VTABLE_PROPERTY_CONST),
9be3455f 654 SD_BUS_PROPERTY("KernelVersion", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_KERNEL_VERSION, SD_BUS_VTABLE_PROPERTY_CONST),
44c32988
DH
655 SD_BUS_PROPERTY("OperatingSystemPrettyName", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_OS_PRETTY_NAME, SD_BUS_VTABLE_PROPERTY_CONST),
656 SD_BUS_PROPERTY("OperatingSystemCPEName", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_OS_CPE_NAME, SD_BUS_VTABLE_PROPERTY_CONST),
adacb957
LP
657 SD_BUS_METHOD("SetHostname", "sb", NULL, method_set_hostname, SD_BUS_VTABLE_UNPRIVILEGED),
658 SD_BUS_METHOD("SetStaticHostname", "sb", NULL, method_set_static_hostname, SD_BUS_VTABLE_UNPRIVILEGED),
659 SD_BUS_METHOD("SetPrettyHostname", "sb", NULL, method_set_pretty_hostname, SD_BUS_VTABLE_UNPRIVILEGED),
660 SD_BUS_METHOD("SetIconName", "sb", NULL, method_set_icon_name, SD_BUS_VTABLE_UNPRIVILEGED),
661 SD_BUS_METHOD("SetChassis", "sb", NULL, method_set_chassis, SD_BUS_VTABLE_UNPRIVILEGED),
799298d6 662 SD_BUS_METHOD("SetDeployment", "sb", NULL, method_set_deployment, SD_BUS_VTABLE_UNPRIVILEGED),
ce0f1493 663 SD_BUS_METHOD("SetLocation", "sb", NULL, method_set_location, SD_BUS_VTABLE_UNPRIVILEGED),
66a4c743
LP
664 SD_BUS_VTABLE_END,
665};
666
667static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) {
4afd3348 668 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
7640a5de
LP
669 int r;
670
66a4c743
LP
671 assert(c);
672 assert(event);
d0baa06f
LP
673 assert(_bus);
674
76b54375 675 r = sd_bus_default_system(&bus);
23bbb0de
MS
676 if (r < 0)
677 return log_error_errno(r, "Failed to get system bus connection: %m");
d0baa06f 678
19befb2d 679 r = sd_bus_add_object_vtable(bus, NULL, "/org/freedesktop/hostname1", "org.freedesktop.hostname1", hostname_vtable, c);
23bbb0de
MS
680 if (r < 0)
681 return log_error_errno(r, "Failed to register object: %m");
d0baa06f 682
0c0b9306 683 r = sd_bus_request_name_async(bus, NULL, "org.freedesktop.hostname1", 0, NULL, NULL);
23bbb0de 684 if (r < 0)
0c0b9306 685 return log_error_errno(r, "Failed to request name: %m");
add10b5a 686
66a4c743 687 r = sd_bus_attach_event(bus, event, 0);
23bbb0de
MS
688 if (r < 0)
689 return log_error_errno(r, "Failed to attach bus to event loop: %m");
d0baa06f 690
1cc6c93a 691 *_bus = TAKE_PTR(bus);
d0baa06f 692
66a4c743 693 return 0;
d0baa06f
LP
694}
695
696int main(int argc, char *argv[]) {
66a4c743 697 Context context = {};
4afd3348
LP
698 _cleanup_(sd_event_unrefp) sd_event *event = NULL;
699 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
d0baa06f 700 int r;
d0baa06f 701
7640a5de
LP
702 log_set_target(LOG_TARGET_AUTO);
703 log_parse_environment();
704 log_open();
705
4c12626c 706 umask(0022);
c3dacc8b 707 mac_selinux_init();
4c12626c 708
7640a5de
LP
709 if (argc != 1) {
710 log_error("This program takes no arguments.");
711 r = -EINVAL;
712 goto finish;
713 }
714
afc6adb5 715 r = sd_event_default(&event);
7640a5de 716 if (r < 0) {
da927ba9 717 log_error_errno(r, "Failed to allocate event loop: %m");
7640a5de
LP
718 goto finish;
719 }
720
cde93897
LP
721 sd_event_set_watchdog(event, true);
722
66a4c743 723 r = connect_bus(&context, event, &bus);
d0baa06f 724 if (r < 0)
7640a5de 725 goto finish;
7640a5de 726
66a4c743
LP
727 r = context_read_data(&context);
728 if (r < 0) {
da927ba9 729 log_error_errno(r, "Failed to read hostname and machine information: %m");
66a4c743
LP
730 goto finish;
731 }
ad740100 732
37224a5f 733 r = bus_event_loop_with_idle(event, bus, "org.freedesktop.hostname1", DEFAULT_EXIT_USEC, NULL, NULL);
66a4c743 734 if (r < 0) {
da927ba9 735 log_error_errno(r, "Failed to run event loop: %m");
66a4c743 736 goto finish;
ad740100 737 }
7640a5de 738
7640a5de 739finish:
36e34057 740 context_free(&context);
7640a5de 741
7640a5de
LP
742 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
743}