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