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