]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/hostname/hostnamed.c
use "Out of memory." consistantly (or with "\n")
[thirdparty/systemd.git] / src / hostname / hostnamed.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2011 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
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
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 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser 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>
27 #include <dlfcn.h>
28
29 #include "util.h"
30 #include "strv.h"
31 #include "dbus-common.h"
32 #include "polkit.h"
33 #include "def.h"
34 #include "virt.h"
35
36 #define INTERFACE \
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
60 #define INTROSPECTION \
61 DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
62 "<node>\n" \
63 INTERFACE \
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
73 const char hostname_interface[] _introspect_("hostname1") = INTERFACE;
74
75 enum {
76 PROP_HOSTNAME,
77 PROP_STATIC_HOSTNAME,
78 PROP_PRETTY_HOSTNAME,
79 PROP_ICON_NAME,
80 _PROP_MAX
81 };
82
83 static char *data[_PROP_MAX] = {
84 NULL,
85 NULL,
86 NULL,
87 NULL
88 };
89
90 static usec_t remain_until = 0;
91
92 static 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
101 static 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
124 static 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
136 static 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
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 */
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:
182 case 0x1C:
183 return "computer-server";
184 }
185
186 #endif
187 return NULL;
188 }
189
190 static 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
204 static 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
214 return write_one_line_file_atomic("/etc/hostname", data[PROP_STATIC_HOSTNAME]);
215 }
216
217 static int write_data_other(void) {
218
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])) {
237 strv_env_unset(l, name[p]);
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
269 static 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
283 static 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
291 static const BusBoundProperties bps[] = {
292 { "org.freedesktop.hostname1", bus_hostname_properties, data },
293 { NULL, }
294 };
295
296 static DBusHandlerResult hostname_message_handler(
297 DBusConnection *connection,
298 DBusMessage *message,
299 void *userdata) {
300
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
335 r = verify_polkit(connection, message, "org.freedesktop.hostname1.set-hostname", interactive, NULL, &error);
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();
347 if (r < 0) {
348 log_error("Failed to set host name: %s", strerror(-r));
349 return bus_send_error_reply(connection, message, NULL, r);
350 }
351
352 log_info("Changed host name to '%s'", strempty(data[PROP_HOSTNAME]));
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
379 r = verify_polkit(connection, message, "org.freedesktop.hostname1.set-static-hostname", interactive, NULL, &error);
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();
401 if (r < 0) {
402 log_error("Failed to write static host name: %s", strerror(-r));
403 return bus_send_error_reply(connection, message, NULL, r);
404 }
405
406 log_info("Changed static host name to '%s'", strempty(data[PROP_STATIC_HOSTNAME]));
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
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" :
444 "org.freedesktop.hostname1.set-machine-info", interactive, NULL, &error);
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();
463 if (r < 0) {
464 log_error("Failed to write machine info: %s", strerror(-r));
465 return bus_send_error_reply(connection, message, NULL, r);
466 }
467
468 log_info("Changed %s to '%s'", k == PROP_PRETTY_HOSTNAME ? "pretty host name" : "icon name", strempty(data[k]));
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
479 return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, bps);
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
500 oom:
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
512 static int connect_bus(DBusConnection **_bus) {
513 static const DBusObjectPathVTable hostname_vtable = {
514 .message_function = hostname_message_handler
515 };
516 DBusError error;
517 DBusConnection *bus = NULL;
518 int r;
519
520 assert(_bus);
521
522 dbus_error_init(&error);
523
524 bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
525 if (!bus) {
526 log_error("Failed to get system D-Bus connection: %s", bus_error_message(&error));
527 r = -ECONNREFUSED;
528 goto fail;
529 }
530
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)) {
535 log_error("Out of memory.");
536 r = -ENOMEM;
537 goto fail;
538 }
539
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.");
549 r = -EEXIST;
550 goto fail;
551 }
552
553 if (_bus)
554 *_bus = bus;
555
556 return 0;
557
558 fail:
559 dbus_connection_close(bus);
560 dbus_connection_unref(bus);
561
562 dbus_error_free(&error);
563
564 return r;
565 }
566
567 int main(int argc, char *argv[]) {
568 int r;
569 DBusConnection *bus = NULL;
570 bool exiting = false;
571
572 log_set_target(LOG_TARGET_AUTO);
573 log_parse_environment();
574 log_open();
575
576 umask(0022);
577
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
586 if (argc != 1) {
587 log_error("This program takes no arguments.");
588 r = -EINVAL;
589 goto finish;
590 }
591
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
595 r = read_data();
596 if (r < 0) {
597 log_error("Failed to read hostname data: %s", strerror(-r));
598 goto finish;
599 }
600
601 r = connect_bus(&bus);
602 if (r < 0)
603 goto finish;
604
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 }
616
617 r = 0;
618
619 finish:
620 free_data();
621
622 if (bus) {
623 dbus_connection_flush(bus);
624 dbus_connection_close(bus);
625 dbus_connection_unref(bus);
626 }
627
628 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
629 }