1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2012 Lennart Poettering
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
30 #include "alloc-util.h"
31 #include "architecture.h"
32 #include "bus-error.h"
34 #include "hostname-util.h"
35 #include "spawn-polkit-agent.h"
38 static bool arg_ask_password
= true;
39 static BusTransport arg_transport
= BUS_TRANSPORT_LOCAL
;
40 static char *arg_host
= NULL
;
41 static bool arg_transient
= false;
42 static bool arg_pretty
= false;
43 static bool arg_static
= false;
45 typedef struct StatusInfo
{
47 char *static_hostname
;
48 char *pretty_hostname
;
61 static void print_status_info(StatusInfo
*i
) {
62 sd_id128_t mid
= {}, bid
= {};
67 printf(" Static hostname: %s\n", strna(i
->static_hostname
));
69 if (!isempty(i
->pretty_hostname
) &&
70 !streq_ptr(i
->pretty_hostname
, i
->static_hostname
))
71 printf(" Pretty hostname: %s\n", i
->pretty_hostname
);
73 if (!isempty(i
->hostname
) &&
74 !streq_ptr(i
->hostname
, i
->static_hostname
))
75 printf("Transient hostname: %s\n", i
->hostname
);
77 if (!isempty(i
->icon_name
))
78 printf(" Icon name: %s\n",
81 if (!isempty(i
->chassis
))
82 printf(" Chassis: %s\n",
85 if (!isempty(i
->deployment
))
86 printf(" Deployment: %s\n", i
->deployment
);
88 if (!isempty(i
->location
))
89 printf(" Location: %s\n", i
->location
);
91 r
= sd_id128_get_machine(&mid
);
93 printf(" Machine ID: " SD_ID128_FORMAT_STR
"\n", SD_ID128_FORMAT_VAL(mid
));
95 r
= sd_id128_get_boot(&bid
);
97 printf(" Boot ID: " SD_ID128_FORMAT_STR
"\n", SD_ID128_FORMAT_VAL(bid
));
99 if (!isempty(i
->virtualization
))
100 printf(" Virtualization: %s\n", i
->virtualization
);
102 if (!isempty(i
->os_pretty_name
))
103 printf(" Operating System: %s\n", i
->os_pretty_name
);
105 if (!isempty(i
->os_cpe_name
))
106 printf(" CPE OS Name: %s\n", i
->os_cpe_name
);
108 if (!isempty(i
->kernel_name
) && !isempty(i
->kernel_release
))
109 printf(" Kernel: %s %s\n", i
->kernel_name
, i
->kernel_release
);
111 if (!isempty(i
->architecture
))
112 printf(" Architecture: %s\n", i
->architecture
);
116 static int show_one_name(sd_bus
*bus
, const char* attr
) {
117 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
118 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
122 r
= sd_bus_get_property(
124 "org.freedesktop.hostname1",
125 "/org/freedesktop/hostname1",
126 "org.freedesktop.hostname1",
128 &error
, &reply
, "s");
130 return log_error_errno(r
, "Could not get property: %s", bus_error_message(&error
, r
));
132 r
= sd_bus_message_read(reply
, "s", &s
);
134 return bus_log_parse_error(r
);
141 static int show_all_names(sd_bus
*bus
, sd_bus_error
*error
) {
142 StatusInfo info
= {};
144 static const struct bus_properties_map hostname_map
[] = {
145 { "Hostname", "s", NULL
, offsetof(StatusInfo
, hostname
) },
146 { "StaticHostname", "s", NULL
, offsetof(StatusInfo
, static_hostname
) },
147 { "PrettyHostname", "s", NULL
, offsetof(StatusInfo
, pretty_hostname
) },
148 { "IconName", "s", NULL
, offsetof(StatusInfo
, icon_name
) },
149 { "Chassis", "s", NULL
, offsetof(StatusInfo
, chassis
) },
150 { "Deployment", "s", NULL
, offsetof(StatusInfo
, deployment
) },
151 { "Location", "s", NULL
, offsetof(StatusInfo
, location
) },
152 { "KernelName", "s", NULL
, offsetof(StatusInfo
, kernel_name
) },
153 { "KernelRelease", "s", NULL
, offsetof(StatusInfo
, kernel_release
) },
154 { "OperatingSystemPrettyName", "s", NULL
, offsetof(StatusInfo
, os_pretty_name
) },
155 { "OperatingSystemCPEName", "s", NULL
, offsetof(StatusInfo
, os_cpe_name
) },
159 static const struct bus_properties_map manager_map
[] = {
160 { "Virtualization", "s", NULL
, offsetof(StatusInfo
, virtualization
) },
161 { "Architecture", "s", NULL
, offsetof(StatusInfo
, architecture
) },
167 r
= bus_map_all_properties(bus
,
168 "org.freedesktop.hostname1",
169 "/org/freedesktop/hostname1",
176 bus_map_all_properties(bus
,
177 "org.freedesktop.systemd1",
178 "/org/freedesktop/systemd1",
183 print_status_info(&info
);
187 free(info
.static_hostname
);
188 free(info
.pretty_hostname
);
189 free(info
.icon_name
);
191 free(info
.deployment
);
193 free(info
.kernel_name
);
194 free(info
.kernel_release
);
195 free(info
.os_pretty_name
);
196 free(info
.os_cpe_name
);
197 free(info
.virtualization
);
198 free(info
.architecture
);
203 static int show_status(sd_bus
*bus
, char **args
, unsigned n
) {
208 if (arg_pretty
|| arg_static
|| arg_transient
) {
211 if (!!arg_static
+ !!arg_pretty
+ !!arg_transient
> 1) {
212 log_error("Cannot query more than one name type at a time");
216 attr
= arg_pretty
? "PrettyHostname" :
217 arg_static
? "StaticHostname" : "Hostname";
219 return show_one_name(bus
, attr
);
221 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
223 r
= show_all_names(bus
, &error
);
225 return log_error_errno(r
, "Failed to query system properties: %s", bus_error_message(&error
, r
));
231 static int set_simple_string(sd_bus
*bus
, const char *method
, const char *value
) {
232 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
235 polkit_agent_open_if_enabled(arg_transport
, arg_ask_password
);
237 r
= sd_bus_call_method(
239 "org.freedesktop.hostname1",
240 "/org/freedesktop/hostname1",
241 "org.freedesktop.hostname1",
244 "sb", value
, arg_ask_password
);
246 log_error("Could not set property: %s", bus_error_message(&error
, -r
));
250 static int set_hostname(sd_bus
*bus
, char **args
, unsigned n
) {
251 _cleanup_free_
char *h
= NULL
;
252 const char *hostname
= args
[1];
258 if (!arg_pretty
&& !arg_static
&& !arg_transient
)
259 arg_pretty
= arg_static
= arg_transient
= true;
264 /* If the passed hostname is already valid, then assume the user doesn't know anything about pretty
265 * hostnames, so let's unset the pretty hostname, and just set the passed hostname as static/dynamic
267 if (arg_static
&& hostname_is_valid(hostname
, true))
268 p
= ""; /* No pretty hostname (as it is redundant), just a static one */
270 p
= hostname
; /* Use the passed name as pretty hostname */
272 r
= set_simple_string(bus
, "SetPrettyHostname", p
);
276 /* Now that we set the pretty hostname, let's clean up the parameter and use that as static
277 * hostname. If the hostname was already valid as static hostname, this will only chop off the trailing
278 * dot if there is one. If it was not valid, then it will be made fully valid by truncating, dropping
279 * multiple dots, and dropping weird chars. Note that we clean the name up only if we also are
280 * supposed to set the pretty name. If the pretty name is not being set we assume the user knows what
281 * he does and pass the name as-is. */
282 h
= strdup(hostname
);
286 hostname
= hostname_cleanup(h
); /* Use the cleaned up name as static hostname */
290 r
= set_simple_string(bus
, "SetStaticHostname", hostname
);
296 r
= set_simple_string(bus
, "SetHostname", hostname
);
304 static int set_icon_name(sd_bus
*bus
, char **args
, unsigned n
) {
308 return set_simple_string(bus
, "SetIconName", args
[1]);
311 static int set_chassis(sd_bus
*bus
, char **args
, unsigned n
) {
315 return set_simple_string(bus
, "SetChassis", args
[1]);
318 static int set_deployment(sd_bus
*bus
, char **args
, unsigned n
) {
322 return set_simple_string(bus
, "SetDeployment", args
[1]);
325 static int set_location(sd_bus
*bus
, char **args
, unsigned n
) {
329 return set_simple_string(bus
, "SetLocation", args
[1]);
332 static void help(void) {
333 printf("%s [OPTIONS...] COMMAND ...\n\n"
334 "Query or change system hostname.\n\n"
335 " -h --help Show this help\n"
336 " --version Show package version\n"
337 " --no-ask-password Do not prompt for password\n"
338 " -H --host=[USER@]HOST Operate on remote host\n"
339 " -M --machine=CONTAINER Operate on local container\n"
340 " --transient Only set transient hostname\n"
341 " --static Only set static hostname\n"
342 " --pretty Only set pretty hostname\n\n"
344 " status Show current hostname settings\n"
345 " set-hostname NAME Set system hostname\n"
346 " set-icon-name NAME Set icon name for host\n"
347 " set-chassis NAME Set chassis type for host\n"
348 " set-deployment NAME Set deployment environment for host\n"
349 " set-location NAME Set location for host\n"
350 , program_invocation_short_name
);
353 static int parse_argv(int argc
, char *argv
[]) {
363 static const struct option options
[] = {
364 { "help", no_argument
, NULL
, 'h' },
365 { "version", no_argument
, NULL
, ARG_VERSION
},
366 { "transient", no_argument
, NULL
, ARG_TRANSIENT
},
367 { "static", no_argument
, NULL
, ARG_STATIC
},
368 { "pretty", no_argument
, NULL
, ARG_PRETTY
},
369 { "host", required_argument
, NULL
, 'H' },
370 { "machine", required_argument
, NULL
, 'M' },
371 { "no-ask-password", no_argument
, NULL
, ARG_NO_ASK_PASSWORD
},
380 while ((c
= getopt_long(argc
, argv
, "hH:M:", options
, NULL
)) >= 0)
392 arg_transport
= BUS_TRANSPORT_REMOTE
;
397 arg_transport
= BUS_TRANSPORT_MACHINE
;
402 arg_transient
= true;
413 case ARG_NO_ASK_PASSWORD
:
414 arg_ask_password
= false;
421 assert_not_reached("Unhandled option");
427 static int hostnamectl_main(sd_bus
*bus
, int argc
, char *argv
[]) {
429 static const struct {
437 int (* const dispatch
)(sd_bus
*bus
, char **args
, unsigned n
);
439 { "status", LESS
, 1, show_status
},
440 { "set-hostname", EQUAL
, 2, set_hostname
},
441 { "set-icon-name", EQUAL
, 2, set_icon_name
},
442 { "set-chassis", EQUAL
, 2, set_chassis
},
443 { "set-deployment", EQUAL
, 2, set_deployment
},
444 { "set-location", EQUAL
, 2, set_location
},
453 left
= argc
- optind
;
456 /* Special rule: no arguments means "status" */
459 if (streq(argv
[optind
], "help")) {
464 for (i
= 0; i
< ELEMENTSOF(verbs
); i
++)
465 if (streq(argv
[optind
], verbs
[i
].verb
))
468 if (i
>= ELEMENTSOF(verbs
)) {
469 log_error("Unknown operation %s", argv
[optind
]);
474 switch (verbs
[i
].argc_cmp
) {
477 if (left
!= verbs
[i
].argc
) {
478 log_error("Invalid number of arguments.");
485 if (left
< verbs
[i
].argc
) {
486 log_error("Too few arguments.");
493 if (left
> verbs
[i
].argc
) {
494 log_error("Too many arguments.");
501 assert_not_reached("Unknown comparison operator.");
504 return verbs
[i
].dispatch(bus
, argv
+ optind
, left
);
507 int main(int argc
, char *argv
[]) {
508 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
511 setlocale(LC_ALL
, "");
512 log_parse_environment();
515 r
= parse_argv(argc
, argv
);
519 r
= bus_connect_transport(arg_transport
, arg_host
, false, &bus
);
521 log_error_errno(r
, "Failed to create bus connection: %m");
525 r
= hostnamectl_main(bus
, argc
, argv
);
528 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;