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