1
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4
This file is part of systemd.
6
Copyright 2011 Lennart Poettering
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.
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.
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/>.
22
#include <dbus/dbus.h>
31
#include "dbus-common.h"
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" \
46
" <method name=\"SetStaticHostname\">\n" \
47
" <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
48
" <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\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" \
54
" <method name=\"SetIconName\">\n" \
55
" <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
56
" <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \
60
#define INTROSPECTION \
61
DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
64
BUS_PROPERTIES_INTERFACE \
65
BUS_INTROSPECTABLE_INTERFACE \
69
#define INTERFACES_LIST \
70
BUS_GENERIC_INTERFACES_LIST \
71
"org.freedesktop.hostname1\0"
73
const char hostname_interface[] _introspect_("hostname1") = INTERFACE;
83
static char *data[_PROP_MAX] = {
90
static usec_t remain_until = 0;
92
static void free_data(void) {
95
for (p = 0; p < _PROP_MAX; p++) {
101
static int read_data(void) {
106
data[PROP_HOSTNAME] = gethostname_malloc();
107
if (!data[PROP_HOSTNAME])
110
r = read_one_line_file("/etc/hostname", &data[PROP_STATIC_HOSTNAME]);
111
if (r < 0 && r != -ENOENT)
114
r = parse_env_file("/etc/machine-info", NEWLINE,
115
"PRETTY_HOSTNAME", &data[PROP_PRETTY_HOSTNAME],
116
"ICON_NAME", &data[PROP_ICON_NAME],
118
if (r < 0 && r != -ENOENT)
124
static bool check_nss(void) {
128
if ((dl = dlopen("libnss_myhostname.so.2", RTLD_LAZY))) {
136
static const char* fallback_icon_name(void) {
138
#if defined(__i386__) || defined(__x86_64__)
144
if (detect_virtualization(NULL) > 0)
145
return "computer-vm";
147
#if defined(__i386__) || defined(__x86_64__)
148
r = read_one_line_file("/sys/class/dmi/id/chassis_type", &type);
152
r = safe_atou(type, &t);
158
/* We only list the really obvious cases here. The DMI data is
159
unreliable enough, so let's not do any additional guesswork
162
See the SMBIOS Specification 2.7.1 section 7.4.1 for
163
details about the values listed here:
165
http://www.dmtf.org/sites/default/files/standards/documents/DSP0134_2.7.1.pdf
174
return "computer-desktop";
179
return "computer-laptop";
183
return "computer-server";
190
static int write_data_hostname(void) {
193
if (isempty(data[PROP_HOSTNAME]))
196
hn = data[PROP_HOSTNAME];
198
if (sethostname(hn, strlen(hn)) < 0)
204
static int write_data_static_hostname(void) {
206
if (isempty(data[PROP_STATIC_HOSTNAME])) {
208
if (unlink("/etc/hostname") < 0)
209
return errno == ENOENT ? 0 : -errno;
214
return write_one_line_file_atomic("/etc/hostname", data[PROP_STATIC_HOSTNAME]);
217
static int write_data_other(void) {
219
static const char * const name[_PROP_MAX] = {
220
[PROP_PRETTY_HOSTNAME] = "PRETTY_HOSTNAME",
221
[PROP_ICON_NAME] = "ICON_NAME"
227
r = load_env_file("/etc/machine-info", &l);
228
if (r < 0 && r != -ENOENT)
231
for (p = 2; p < _PROP_MAX; p++) {
236
if (isempty(data[p])) {
237
strv_env_unset(l, name[p]);
241
if (asprintf(&t, "%s=%s", name[p], strempty(data[p])) < 0) {
246
u = strv_env_set(l, t);
255
if (strv_isempty(l)) {
257
if (unlink("/etc/machine-info") < 0)
258
return errno == ENOENT ? 0 : -errno;
263
r = write_env_file("/etc/machine-info", l);
269
static int bus_hostname_append_icon_name(DBusMessageIter *i, const char *property, void *userdata) {
275
if (isempty(data[PROP_ICON_NAME]))
276
name = fallback_icon_name();
278
name = data[PROP_ICON_NAME];
280
return bus_property_append_string(i, property, (void*) name);
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 },
291
static const BusBoundProperties bps[] = {
292
{ "org.freedesktop.hostname1", bus_hostname_properties, data },
296
static DBusHandlerResult hostname_message_handler(
297
DBusConnection *connection,
298
DBusMessage *message,
302
DBusMessage *reply = NULL, *changed = NULL;
309
dbus_error_init(&error);
311
if (dbus_message_is_method_call(message, "org.freedesktop.hostname1", "SetHostname")) {
313
dbus_bool_t interactive;
315
if (!dbus_message_get_args(
318
DBUS_TYPE_STRING, &name,
319
DBUS_TYPE_BOOLEAN, &interactive,
321
return bus_send_error_reply(connection, message, &error, -EINVAL);
324
name = data[PROP_STATIC_HOSTNAME];
329
if (!hostname_is_valid(name))
330
return bus_send_error_reply(connection, message, NULL, -EINVAL);
332
if (!streq_ptr(name, data[PROP_HOSTNAME])) {
335
r = verify_polkit(connection, message, "org.freedesktop.hostname1.set-hostname", interactive, NULL, &error);
337
return bus_send_error_reply(connection, message, &error, r);
343
free(data[PROP_HOSTNAME]);
344
data[PROP_HOSTNAME] = h;
346
r = write_data_hostname();
348
log_error("Failed to set host name: %s", strerror(-r));
349
return bus_send_error_reply(connection, message, NULL, r);
352
log_info("Changed host name to '%s'", strempty(data[PROP_HOSTNAME]));
354
changed = bus_properties_changed_new(
355
"/org/freedesktop/hostname1",
356
"org.freedesktop.hostname1",
362
} else if (dbus_message_is_method_call(message, "org.freedesktop.hostname1", "SetStaticHostname")) {
364
dbus_bool_t interactive;
366
if (!dbus_message_get_args(
369
DBUS_TYPE_STRING, &name,
370
DBUS_TYPE_BOOLEAN, &interactive,
372
return bus_send_error_reply(connection, message, &error, -EINVAL);
377
if (!streq_ptr(name, data[PROP_STATIC_HOSTNAME])) {
379
r = verify_polkit(connection, message, "org.freedesktop.hostname1.set-static-hostname", interactive, NULL, &error);
381
return bus_send_error_reply(connection, message, &error, r);
384
free(data[PROP_STATIC_HOSTNAME]);
385
data[PROP_STATIC_HOSTNAME] = NULL;
389
if (!hostname_is_valid(name))
390
return bus_send_error_reply(connection, message, NULL, -EINVAL);
396
free(data[PROP_STATIC_HOSTNAME]);
397
data[PROP_STATIC_HOSTNAME] = h;
400
r = write_data_static_hostname();
402
log_error("Failed to write static host name: %s", strerror(-r));
403
return bus_send_error_reply(connection, message, NULL, r);
406
log_info("Changed static host name to '%s'", strempty(data[PROP_STATIC_HOSTNAME]));
408
changed = bus_properties_changed_new(
409
"/org/freedesktop/hostname1",
410
"org.freedesktop.hostname1",
416
} else if (dbus_message_is_method_call(message, "org.freedesktop.hostname1", "SetPrettyHostname") ||
417
dbus_message_is_method_call(message, "org.freedesktop.hostname1", "SetIconName")) {
420
dbus_bool_t interactive;
423
if (!dbus_message_get_args(
426
DBUS_TYPE_STRING, &name,
427
DBUS_TYPE_BOOLEAN, &interactive,
429
return bus_send_error_reply(connection, message, &error, -EINVAL);
434
k = streq(dbus_message_get_member(message), "SetPrettyHostname") ? PROP_PRETTY_HOSTNAME : PROP_ICON_NAME;
436
if (!streq_ptr(name, data[k])) {
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... */
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);
446
return bus_send_error_reply(connection, message, &error, r);
462
r = write_data_other();
464
log_error("Failed to write machine info: %s", strerror(-r));
465
return bus_send_error_reply(connection, message, NULL, r);
468
log_info("Changed %s to '%s'", k == PROP_PRETTY_HOSTNAME ? "pretty host name" : "icon name", strempty(data[k]));
470
changed = bus_properties_changed_new(
471
"/org/freedesktop/hostname1",
472
"org.freedesktop.hostname1",
473
k == PROP_PRETTY_HOSTNAME ? "PrettyHostname\0" : "IconName\0");
479
return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, bps);
481
if (!(reply = dbus_message_new_method_return(message)))
484
if (!dbus_connection_send(connection, reply, NULL))
487
dbus_message_unref(reply);
492
if (!dbus_connection_send(connection, changed, NULL))
495
dbus_message_unref(changed);
498
return DBUS_HANDLER_RESULT_HANDLED;
502
dbus_message_unref(reply);
505
dbus_message_unref(changed);
507
dbus_error_free(&error);
509
return DBUS_HANDLER_RESULT_NEED_MEMORY;
512
static int connect_bus(DBusConnection **_bus) {
513
static const DBusObjectPathVTable hostname_vtable = {
514
.message_function = hostname_message_handler
517
DBusConnection *bus = NULL;
522
dbus_error_init(&error);
524
bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
526
log_error("Failed to get system D-Bus connection: %s", bus_error_message(&error));
531
dbus_connection_set_exit_on_disconnect(bus, FALSE);
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("Not enough memory");
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));
547
if (r != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
548
log_error("Failed to acquire name.");
559
dbus_connection_close(bus);
560
dbus_connection_unref(bus);
562
dbus_error_free(&error);
567
int main(int argc, char *argv[]) {
569
DBusConnection *bus = NULL;
570
bool exiting = false;
572
log_set_target(LOG_TARGET_AUTO);
573
log_parse_environment();
578
if (argc == 2 && streq(argv[1], "--introspect")) {
579
fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
581
fputs(hostname_interface, stdout);
582
fputs("</node>\n", stdout);
587
log_error("This program takes no arguments.");
593
log_warning("Warning: nss-myhostname is not installed. Changing the local hostname might make it unresolveable. Please install nss-myhostname!");
597
log_error("Failed to read hostname data: %s", strerror(-r));
601
r = connect_bus(&bus);
605
remain_until = now(CLOCK_MONOTONIC) + DEFAULT_EXIT_USEC;
608
if (!dbus_connection_read_write_dispatch(bus, exiting ? -1 : (int) (DEFAULT_EXIT_USEC/USEC_PER_MSEC)))
611
if (!exiting && remain_until < now(CLOCK_MONOTONIC)) {
613
bus_async_unregister_and_exit(bus, "org.freedesktop.hostname1");
623
dbus_connection_flush(bus);
624
dbus_connection_close(bus);
625
dbus_connection_unref(bus);
628
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;