]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/hostnamed.c
man: list specifiers in a table
[thirdparty/systemd.git] / src / hostnamed.c
CommitLineData
7640a5de
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
41550d40 6 Copyright 2011 Lennart Poettering
7640a5de
LP
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
22#include <dbus/dbus.h>
23
24#include <errno.h>
25#include <string.h>
26#include <unistd.h>
c2a14cf0 27#include <dlfcn.h>
7640a5de
LP
28
29#include "util.h"
30#include "strv.h"
31#include "dbus-common.h"
f401e48c 32#include "polkit.h"
ad740100 33#include "def.h"
7640a5de 34
91f9dcaf 35#define INTERFACE \
7640a5de
LP
36 " <interface name=\"org.freedesktop.hostname1\">\n" \
37 " <property name=\"Hostname\" type=\"s\" access=\"read\"/>\n" \
38 " <property name=\"StaticHostname\" type=\"s\" access=\"read\"/>\n" \
39 " <property name=\"PrettyHostname\" type=\"s\" access=\"read\"/>\n" \
40 " <property name=\"IconName\" type=\"s\" access=\"read\"/>\n" \
41 " <method name=\"SetHostname\">\n" \
42 " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
43 " <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \
44 " </method>\n" \
45 " <method name=\"SetStaticHostname\">\n" \
46 " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
47 " <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \
48 " </method>\n" \
49 " <method name=\"SetPrettyHostname\">\n" \
50 " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
51 " <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \
52 " </method>\n" \
53 " <method name=\"SetIconName\">\n" \
54 " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
55 " <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \
56 " </method>\n" \
91f9dcaf
LP
57 " </interface>\n"
58
59#define INTROSPECTION \
60 DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
61 "<node>\n" \
62 INTERFACE \
7640a5de
LP
63 BUS_PROPERTIES_INTERFACE \
64 BUS_INTROSPECTABLE_INTERFACE \
65 BUS_PEER_INTERFACE \
66 "</node>\n"
67
68#define INTERFACES_LIST \
69 BUS_GENERIC_INTERFACES_LIST \
70 "org.freedesktop.hostname1\0"
71
91f9dcaf
LP
72const char hostname_interface[] _introspect_("hostname1") = INTERFACE;
73
7640a5de
LP
74enum {
75 PROP_HOSTNAME,
76 PROP_STATIC_HOSTNAME,
77 PROP_PRETTY_HOSTNAME,
78 PROP_ICON_NAME,
79 _PROP_MAX
80};
81
82static char *data[_PROP_MAX] = {
83 NULL,
84 NULL,
85 NULL,
86 NULL
87};
88
ad740100
LP
89static usec_t remain_until = 0;
90
7640a5de
LP
91static void free_data(void) {
92 int p;
93
94 for (p = 0; p < _PROP_MAX; p++) {
95 free(data[p]);
96 data[p] = NULL;
97 }
98}
99
100static int read_data(void) {
101 int r;
102
103 free_data();
104
105 data[PROP_HOSTNAME] = gethostname_malloc();
106 if (!data[PROP_HOSTNAME])
107 return -ENOMEM;
108
109 r = read_one_line_file("/etc/hostname", &data[PROP_STATIC_HOSTNAME]);
110 if (r < 0 && r != -ENOENT)
111 return r;
112
113 r = parse_env_file("/etc/machine-info", NEWLINE,
114 "PRETTY_HOSTNAME", &data[PROP_PRETTY_HOSTNAME],
115 "ICON_NAME", &data[PROP_ICON_NAME],
116 NULL);
117 if (r < 0 && r != -ENOENT)
118 return r;
119
120 return 0;
121}
122
c2a14cf0
LP
123static bool check_nss(void) {
124
125 void *dl;
126
127 if ((dl = dlopen("libnss_myhostname.so.2", RTLD_LAZY))) {
128 dlclose(dl);
129 return true;
130 }
131
132 return false;
133}
134
7640a5de
LP
135static const char* fallback_icon_name(void) {
136
137#if defined(__i386__) || defined(__x86_64__)
138 int r;
139 char *type;
140 unsigned t;
141#endif
142
143 if (detect_virtualization(NULL) > 0)
144 return "computer-vm";
145
146#if defined(__i386__) || defined(__x86_64__)
147 r = read_one_line_file("/sys/class/dmi/id/chassis_type", &type);
148 if (r < 0)
149 return NULL;
150
151 r = safe_atou(type, &t);
152 free(type);
153
154 if (r < 0)
155 return NULL;
156
157 /* We only list the really obvious cases here. The DMI data is
158 unreliable enough, so let's not do any additional guesswork
41550d40
LP
159 on top of that.
160
161 See the SMBIOS Specification 2.7.1 section 7.4.1 for
162 details about the values listed here:
163
164 http://www.dmtf.org/sites/default/files/standards/documents/DSP0134_2.7.1.pdf
165 */
7640a5de
LP
166
167 switch (t) {
168
169 case 0x3:
170 case 0x4:
171 case 0x6:
172 case 0x7:
173 return "computer-desktop";
174
175 case 0x9:
176 case 0xA:
177 case 0xE:
178 return "computer-laptop";
179
180 case 0x11:
41550d40 181 case 0x1C:
7640a5de
LP
182 return "computer-server";
183 }
184
185#endif
186 return NULL;
187}
188
189static int write_data_hostname(void) {
190 const char *hn;
191
192 if (isempty(data[PROP_HOSTNAME]))
193 hn = "localhost";
194 else
195 hn = data[PROP_HOSTNAME];
196
197 if (sethostname(hn, strlen(hn)) < 0)
198 return -errno;
199
200 return 0;
201}
202
203static int write_data_static_hostname(void) {
204
205 if (isempty(data[PROP_STATIC_HOSTNAME])) {
206
207 if (unlink("/etc/hostname") < 0)
208 return errno == ENOENT ? 0 : -errno;
209
210 return 0;
211 }
212
34ca941c 213 return write_one_line_file_atomic("/etc/hostname", data[PROP_STATIC_HOSTNAME]);
7640a5de
LP
214}
215
216static int write_data_other(void) {
4f34ed54 217
7640a5de
LP
218 static const char * const name[_PROP_MAX] = {
219 [PROP_PRETTY_HOSTNAME] = "PRETTY_HOSTNAME",
220 [PROP_ICON_NAME] = "ICON_NAME"
221 };
222
223 char **l = NULL;
224 int r, p;
225
226 r = load_env_file("/etc/machine-info", &l);
227 if (r < 0 && r != -ENOENT)
228 return r;
229
230 for (p = 2; p < _PROP_MAX; p++) {
231 char *t, **u;
232
233 assert(name[p]);
234
235 if (isempty(data[p])) {
236 l = strv_env_unset(l, name[p]);
237 continue;
238 }
239
240 if (asprintf(&t, "%s=%s", name[p], strempty(data[p])) < 0) {
241 strv_free(l);
242 return -ENOMEM;
243 }
244
245 u = strv_env_set(l, t);
246 free(t);
247 strv_free(l);
248
249 if (!u)
250 return -ENOMEM;
251 l = u;
252 }
253
254 if (strv_isempty(l)) {
255
256 if (unlink("/etc/machine-info") < 0)
257 return errno == ENOENT ? 0 : -errno;
258
259 return 0;
260 }
261
262 r = write_env_file("/etc/machine-info", l);
263 strv_free(l);
264
265 return r;
266}
267
7640a5de
LP
268static int bus_hostname_append_icon_name(DBusMessageIter *i, const char *property, void *userdata) {
269 const char *name;
270
271 assert(i);
272 assert(property);
273
274 if (isempty(data[PROP_ICON_NAME]))
275 name = fallback_icon_name();
276 else
277 name = data[PROP_ICON_NAME];
278
279 return bus_property_append_string(i, property, (void*) name);
280}
281
282static DBusHandlerResult hostname_message_handler(
283 DBusConnection *connection,
284 DBusMessage *message,
285 void *userdata) {
286
287 const BusProperty properties[] = {
288 { "org.freedesktop.hostname1", "Hostname", bus_property_append_string, "s", data[PROP_HOSTNAME]},
289 { "org.freedesktop.hostname1", "StaticHostname", bus_property_append_string, "s", data[PROP_STATIC_HOSTNAME]},
290 { "org.freedesktop.hostname1", "PrettyHostname", bus_property_append_string, "s", data[PROP_PRETTY_HOSTNAME]},
291 { "org.freedesktop.hostname1", "IconName", bus_hostname_append_icon_name, "s", data[PROP_ICON_NAME]},
292 { NULL, NULL, NULL, NULL, NULL }
293 };
294
295 DBusMessage *reply = NULL, *changed = NULL;
296 DBusError error;
297 int r;
298
299 assert(connection);
300 assert(message);
301
302 dbus_error_init(&error);
303
304 if (dbus_message_is_method_call(message, "org.freedesktop.hostname1", "SetHostname")) {
305 const char *name;
306 dbus_bool_t interactive;
307
308 if (!dbus_message_get_args(
309 message,
310 &error,
311 DBUS_TYPE_STRING, &name,
312 DBUS_TYPE_BOOLEAN, &interactive,
313 DBUS_TYPE_INVALID))
314 return bus_send_error_reply(connection, message, &error, -EINVAL);
315
316 if (isempty(name))
317 name = data[PROP_STATIC_HOSTNAME];
318
319 if (isempty(name))
320 name = "localhost";
321
322 if (!hostname_is_valid(name))
323 return bus_send_error_reply(connection, message, NULL, -EINVAL);
324
325 if (!streq_ptr(name, data[PROP_HOSTNAME])) {
326 char *h;
327
328 r = verify_polkit(connection, message, "org.freedesktop.hostname1.set-hostname", interactive, &error);
329 if (r < 0)
330 return bus_send_error_reply(connection, message, &error, r);
331
332 h = strdup(name);
333 if (!h)
334 goto oom;
335
336 free(data[PROP_HOSTNAME]);
337 data[PROP_HOSTNAME] = h;
338
339 r = write_data_hostname();
4f34ed54
LP
340 if (r < 0) {
341 log_error("Failed to set host name: %s", strerror(-r));
7640a5de 342 return bus_send_error_reply(connection, message, NULL, r);
4f34ed54 343 }
7640a5de 344
4f34ed54 345 log_info("Changed host name to '%s'", strempty(data[PROP_HOSTNAME]));
7640a5de
LP
346
347 changed = bus_properties_changed_new(
348 "/org/freedesktop/hostname1",
349 "org.freedesktop.hostname1",
350 "Hostname\0");
351 if (!changed)
352 goto oom;
353 }
354
355 } else if (dbus_message_is_method_call(message, "org.freedesktop.hostname1", "SetStaticHostname")) {
356 const char *name;
357 dbus_bool_t interactive;
358
359 if (!dbus_message_get_args(
360 message,
361 &error,
362 DBUS_TYPE_STRING, &name,
363 DBUS_TYPE_BOOLEAN, &interactive,
364 DBUS_TYPE_INVALID))
365 return bus_send_error_reply(connection, message, &error, -EINVAL);
366
367 if (isempty(name))
368 name = NULL;
369
370 if (!streq_ptr(name, data[PROP_STATIC_HOSTNAME])) {
371
372 r = verify_polkit(connection, message, "org.freedesktop.hostname1.set-static-hostname", interactive, &error);
373 if (r < 0)
374 return bus_send_error_reply(connection, message, &error, r);
375
376 if (isempty(name)) {
377 free(data[PROP_STATIC_HOSTNAME]);
378 data[PROP_STATIC_HOSTNAME] = NULL;
379 } else {
380 char *h;
381
382 if (!hostname_is_valid(name))
383 return bus_send_error_reply(connection, message, NULL, -EINVAL);
384
385 h = strdup(name);
386 if (!h)
387 goto oom;
388
389 free(data[PROP_STATIC_HOSTNAME]);
390 data[PROP_STATIC_HOSTNAME] = h;
391 }
392
393 r = write_data_static_hostname();
4f34ed54
LP
394 if (r < 0) {
395 log_error("Failed to write static host name: %s", strerror(-r));
7640a5de 396 return bus_send_error_reply(connection, message, NULL, r);
4f34ed54 397 }
7640a5de 398
4f34ed54 399 log_info("Changed static host name to '%s'", strempty(data[PROP_HOSTNAME]));
7640a5de
LP
400
401 changed = bus_properties_changed_new(
402 "/org/freedesktop/hostname1",
403 "org.freedesktop.hostname1",
404 "StaticHostname\0");
405 if (!changed)
406 goto oom;
407 }
408
409 } else if (dbus_message_is_method_call(message, "org.freedesktop.hostname1", "SetPrettyHostname") ||
410 dbus_message_is_method_call(message, "org.freedesktop.hostname1", "SetIconName")) {
411
412 const char *name;
413 dbus_bool_t interactive;
414 int k;
415
416 if (!dbus_message_get_args(
417 message,
418 &error,
419 DBUS_TYPE_STRING, &name,
420 DBUS_TYPE_BOOLEAN, &interactive,
421 DBUS_TYPE_INVALID))
422 return bus_send_error_reply(connection, message, &error, -EINVAL);
423
424 if (isempty(name))
425 name = NULL;
426
427 k = streq(dbus_message_get_member(message), "SetPrettyHostname") ? PROP_PRETTY_HOSTNAME : PROP_ICON_NAME;
428
429 if (!streq_ptr(name, data[k])) {
430
88a07670
LP
431 /* Since the pretty hostname should always be
432 * changed at the same time as the static one,
433 * use the same policy action for both... */
434
435 r = verify_polkit(connection, message, k == PROP_PRETTY_HOSTNAME ?
436 "org.freedesktop.hostname1.set-static-hostname" :
437 "org.freedesktop.hostname1.set-machine-info", interactive, &error);
7640a5de
LP
438 if (r < 0)
439 return bus_send_error_reply(connection, message, &error, r);
440
441 if (isempty(name)) {
442 free(data[k]);
443 data[k] = NULL;
444 } else {
445 char *h;
446
447 h = strdup(name);
448 if (!h)
449 goto oom;
450
451 free(data[k]);
452 data[k] = h;
453 }
454
455 r = write_data_other();
4f34ed54
LP
456 if (r < 0) {
457 log_error("Failed to write machine info: %s", strerror(-r));
7640a5de 458 return bus_send_error_reply(connection, message, NULL, r);
4f34ed54 459 }
7640a5de 460
4f34ed54 461 log_info("Changed %s to '%s'", k == PROP_PRETTY_HOSTNAME ? "pretty host name" : "icon name", strempty(data[k]));
7640a5de
LP
462
463 changed = bus_properties_changed_new(
464 "/org/freedesktop/hostname1",
465 "org.freedesktop.hostname1",
466 k == PROP_PRETTY_HOSTNAME ? "PrettyHostname\0" : "IconName\0");
467 if (!changed)
468 goto oom;
469 }
470
471 } else
472 return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, properties);
473
474 if (!(reply = dbus_message_new_method_return(message)))
475 goto oom;
476
477 if (!dbus_connection_send(connection, reply, NULL))
478 goto oom;
479
480 dbus_message_unref(reply);
481 reply = NULL;
482
483 if (changed) {
484
485 if (!dbus_connection_send(connection, changed, NULL))
486 goto oom;
487
488 dbus_message_unref(changed);
489 }
490
491 return DBUS_HANDLER_RESULT_HANDLED;
492
493oom:
494 if (reply)
495 dbus_message_unref(reply);
496
497 if (changed)
498 dbus_message_unref(changed);
499
500 dbus_error_free(&error);
501
502 return DBUS_HANDLER_RESULT_NEED_MEMORY;
503}
504
d0baa06f
LP
505static int connect_bus(DBusConnection **_bus) {
506 static const DBusObjectPathVTable hostname_vtable = {
7640a5de
LP
507 .message_function = hostname_message_handler
508 };
7640a5de 509 DBusError error;
d0baa06f 510 DBusConnection *bus = NULL;
7640a5de
LP
511 int r;
512
d0baa06f
LP
513 assert(_bus);
514
7640a5de
LP
515 dbus_error_init(&error);
516
d0baa06f
LP
517 bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
518 if (!bus) {
a2e52832 519 log_error("Failed to get system D-Bus connection: %s", bus_error_message(&error));
d0baa06f
LP
520 r = -ECONNREFUSED;
521 goto fail;
522 }
523
ad740100
LP
524 dbus_connection_set_exit_on_disconnect(bus, FALSE);
525
526 if (!dbus_connection_register_object_path(bus, "/org/freedesktop/hostname1", &hostname_vtable, NULL) ||
527 !dbus_connection_add_filter(bus, bus_exit_idle_filter, &remain_until, NULL)) {
d0baa06f
LP
528 log_error("Not enough memory");
529 r = -ENOMEM;
530 goto fail;
531 }
532
add10b5a
LP
533 r = dbus_bus_request_name(bus, "org.freedesktop.hostname1", DBUS_NAME_FLAG_DO_NOT_QUEUE, &error);
534 if (dbus_error_is_set(&error)) {
535 log_error("Failed to register name on bus: %s", bus_error_message(&error));
536 r = -EEXIST;
537 goto fail;
538 }
539
540 if (r != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
541 log_error("Failed to acquire name.");
d0baa06f
LP
542 r = -EEXIST;
543 goto fail;
544 }
545
546 if (_bus)
547 *_bus = bus;
548
549 return 0;
550
551fail:
552 dbus_connection_close(bus);
553 dbus_connection_unref(bus);
554
555 dbus_error_free(&error);
556
557 return r;
558}
559
560int main(int argc, char *argv[]) {
561 int r;
562 DBusConnection *bus = NULL;
ad740100 563 bool exiting = false;
d0baa06f 564
7640a5de
LP
565 log_set_target(LOG_TARGET_AUTO);
566 log_parse_environment();
567 log_open();
568
4c12626c
LP
569 umask(0022);
570
91f9dcaf
LP
571 if (argc == 2 && streq(argv[1], "--introspect")) {
572 fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
573 "<node>\n", stdout);
574 fputs(hostname_interface, stdout);
575 fputs("</node>\n", stdout);
576 return 0;
577 }
578
7640a5de
LP
579 if (argc != 1) {
580 log_error("This program takes no arguments.");
581 r = -EINVAL;
582 goto finish;
583 }
584
c2a14cf0
LP
585 if (!check_nss())
586 log_warning("Warning: nss-myhostname is not installed. Changing the local hostname might make it unresolveable. Please install nss-myhostname!");
587
7640a5de
LP
588 r = read_data();
589 if (r < 0) {
590 log_error("Failed to read hostname data: %s", strerror(-r));
591 goto finish;
592 }
593
d0baa06f
LP
594 r = connect_bus(&bus);
595 if (r < 0)
7640a5de 596 goto finish;
7640a5de 597
ad740100
LP
598 remain_until = now(CLOCK_MONOTONIC) + DEFAULT_EXIT_USEC;
599 for (;;) {
600
601 if (!dbus_connection_read_write_dispatch(bus, exiting ? -1 : (int) (DEFAULT_EXIT_USEC/USEC_PER_MSEC)))
602 break;
603
604 if (!exiting && remain_until < now(CLOCK_MONOTONIC)) {
605 exiting = true;
606 bus_async_unregister_and_exit(bus, "org.freedesktop.hostname1");
607 }
608 }
7640a5de
LP
609
610 r = 0;
611
612finish:
613 free_data();
614
615 if (bus) {
616 dbus_connection_flush(bus);
617 dbus_connection_close(bus);
618 dbus_connection_unref(bus);
619 }
620
7640a5de
LP
621 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
622}