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