Line data Source code
1 : /* SPDX-License-Identifier: LGPL-2.1+ */
2 :
3 : #include <getopt.h>
4 : #include <locale.h>
5 : #include <stdbool.h>
6 : #include <stdlib.h>
7 : #include <string.h>
8 :
9 : #include "sd-bus.h"
10 : #include "sd-id128.h"
11 :
12 : #include "alloc-util.h"
13 : #include "architecture.h"
14 : #include "bus-error.h"
15 : #include "bus-util.h"
16 : #include "hostname-util.h"
17 : #include "main-func.h"
18 : #include "pretty-print.h"
19 : #include "spawn-polkit-agent.h"
20 : #include "util.h"
21 : #include "verbs.h"
22 :
23 : static bool arg_ask_password = true;
24 : static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
25 : static char *arg_host = NULL;
26 : static bool arg_transient = false;
27 : static bool arg_pretty = false;
28 : static bool arg_static = false;
29 :
30 : typedef struct StatusInfo {
31 : const char *hostname;
32 : const char *static_hostname;
33 : const char *pretty_hostname;
34 : const char *icon_name;
35 : const char *chassis;
36 : const char *deployment;
37 : const char *location;
38 : const char *kernel_name;
39 : const char *kernel_release;
40 : const char *os_pretty_name;
41 : const char *os_cpe_name;
42 : const char *virtualization;
43 : const char *architecture;
44 : const char *home_url;
45 : } StatusInfo;
46 :
47 0 : static void print_status_info(StatusInfo *i) {
48 0 : sd_id128_t mid = {}, bid = {};
49 : int r;
50 :
51 0 : assert(i);
52 :
53 0 : printf(" Static hostname: %s\n", strna(i->static_hostname));
54 :
55 0 : if (!isempty(i->pretty_hostname) &&
56 0 : !streq_ptr(i->pretty_hostname, i->static_hostname))
57 0 : printf(" Pretty hostname: %s\n", i->pretty_hostname);
58 :
59 0 : if (!isempty(i->hostname) &&
60 0 : !streq_ptr(i->hostname, i->static_hostname))
61 0 : printf("Transient hostname: %s\n", i->hostname);
62 :
63 0 : if (!isempty(i->icon_name))
64 0 : printf(" Icon name: %s\n",
65 : strna(i->icon_name));
66 :
67 0 : if (!isempty(i->chassis))
68 0 : printf(" Chassis: %s\n",
69 : strna(i->chassis));
70 :
71 0 : if (!isempty(i->deployment))
72 0 : printf(" Deployment: %s\n", i->deployment);
73 :
74 0 : if (!isempty(i->location))
75 0 : printf(" Location: %s\n", i->location);
76 :
77 0 : r = sd_id128_get_machine(&mid);
78 0 : if (r >= 0)
79 0 : printf(" Machine ID: " SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(mid));
80 :
81 0 : r = sd_id128_get_boot(&bid);
82 0 : if (r >= 0)
83 0 : printf(" Boot ID: " SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(bid));
84 :
85 0 : if (!isempty(i->virtualization))
86 0 : printf(" Virtualization: %s\n", i->virtualization);
87 :
88 0 : if (!isempty(i->os_pretty_name)) {
89 0 : _cleanup_free_ char *formatted = NULL;
90 0 : const char *t = i->os_pretty_name;
91 :
92 0 : if (i->home_url) {
93 0 : if (terminal_urlify(i->home_url, i->os_pretty_name, &formatted) >= 0)
94 0 : t = formatted;
95 : }
96 :
97 0 : printf(" Operating System: %s\n", t);
98 : }
99 :
100 0 : if (!isempty(i->os_cpe_name))
101 0 : printf(" CPE OS Name: %s\n", i->os_cpe_name);
102 :
103 0 : if (!isempty(i->kernel_name) && !isempty(i->kernel_release))
104 0 : printf(" Kernel: %s %s\n", i->kernel_name, i->kernel_release);
105 :
106 0 : if (!isempty(i->architecture))
107 0 : printf(" Architecture: %s\n", i->architecture);
108 :
109 0 : }
110 :
111 0 : static int show_one_name(sd_bus *bus, const char* attr) {
112 0 : _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
113 0 : _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
114 : const char *s;
115 : int r;
116 :
117 0 : r = sd_bus_get_property(
118 : bus,
119 : "org.freedesktop.hostname1",
120 : "/org/freedesktop/hostname1",
121 : "org.freedesktop.hostname1",
122 : attr,
123 : &error, &reply, "s");
124 0 : if (r < 0)
125 0 : return log_error_errno(r, "Could not get property: %s", bus_error_message(&error, r));
126 :
127 0 : r = sd_bus_message_read(reply, "s", &s);
128 0 : if (r < 0)
129 0 : return bus_log_parse_error(r);
130 :
131 0 : printf("%s\n", s);
132 :
133 0 : return 0;
134 : }
135 :
136 0 : static int show_all_names(sd_bus *bus, sd_bus_error *error) {
137 0 : StatusInfo info = {};
138 :
139 : static const struct bus_properties_map hostname_map[] = {
140 : { "Hostname", "s", NULL, offsetof(StatusInfo, hostname) },
141 : { "StaticHostname", "s", NULL, offsetof(StatusInfo, static_hostname) },
142 : { "PrettyHostname", "s", NULL, offsetof(StatusInfo, pretty_hostname) },
143 : { "IconName", "s", NULL, offsetof(StatusInfo, icon_name) },
144 : { "Chassis", "s", NULL, offsetof(StatusInfo, chassis) },
145 : { "Deployment", "s", NULL, offsetof(StatusInfo, deployment) },
146 : { "Location", "s", NULL, offsetof(StatusInfo, location) },
147 : { "KernelName", "s", NULL, offsetof(StatusInfo, kernel_name) },
148 : { "KernelRelease", "s", NULL, offsetof(StatusInfo, kernel_release) },
149 : { "OperatingSystemPrettyName", "s", NULL, offsetof(StatusInfo, os_pretty_name) },
150 : { "OperatingSystemCPEName", "s", NULL, offsetof(StatusInfo, os_cpe_name) },
151 : { "HomeURL", "s", NULL, offsetof(StatusInfo, home_url) },
152 : {}
153 : };
154 :
155 : static const struct bus_properties_map manager_map[] = {
156 : { "Virtualization", "s", NULL, offsetof(StatusInfo, virtualization) },
157 : { "Architecture", "s", NULL, offsetof(StatusInfo, architecture) },
158 : {}
159 : };
160 :
161 0 : _cleanup_(sd_bus_message_unrefp) sd_bus_message *host_message = NULL, *manager_message = NULL;
162 : int r;
163 :
164 0 : r = bus_map_all_properties(bus,
165 : "org.freedesktop.hostname1",
166 : "/org/freedesktop/hostname1",
167 : hostname_map,
168 : 0,
169 : error,
170 : &host_message,
171 : &info);
172 0 : if (r < 0)
173 0 : return r;
174 :
175 0 : r = bus_map_all_properties(bus,
176 : "org.freedesktop.systemd1",
177 : "/org/freedesktop/systemd1",
178 : manager_map,
179 : 0,
180 : error,
181 : &manager_message,
182 : &info);
183 :
184 0 : print_status_info(&info);
185 :
186 0 : return r;
187 : }
188 :
189 0 : static int show_status(int argc, char **argv, void *userdata) {
190 0 : sd_bus *bus = userdata;
191 : int r;
192 :
193 0 : if (arg_pretty || arg_static || arg_transient) {
194 : const char *attr;
195 :
196 0 : if (!!arg_static + !!arg_pretty + !!arg_transient > 1) {
197 0 : log_error("Cannot query more than one name type at a time");
198 0 : return -EINVAL;
199 : }
200 :
201 0 : attr = arg_pretty ? "PrettyHostname" :
202 0 : arg_static ? "StaticHostname" : "Hostname";
203 :
204 0 : return show_one_name(bus, attr);
205 : } else {
206 0 : _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
207 :
208 0 : r = show_all_names(bus, &error);
209 0 : if (r < 0)
210 0 : return log_error_errno(r, "Failed to query system properties: %s", bus_error_message(&error, r));
211 :
212 0 : return 0;
213 : }
214 : }
215 :
216 0 : static int set_simple_string(sd_bus *bus, const char *method, const char *value) {
217 0 : _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
218 0 : int r = 0;
219 :
220 0 : polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
221 :
222 0 : r = sd_bus_call_method(
223 : bus,
224 : "org.freedesktop.hostname1",
225 : "/org/freedesktop/hostname1",
226 : "org.freedesktop.hostname1",
227 : method,
228 : &error, NULL,
229 : "sb", value, arg_ask_password);
230 0 : if (r < 0)
231 0 : return log_error_errno(r, "Could not set property: %s", bus_error_message(&error, -r));
232 :
233 0 : return 0;
234 : }
235 :
236 0 : static int set_hostname(int argc, char **argv, void *userdata) {
237 0 : _cleanup_free_ char *h = NULL;
238 0 : const char *hostname = argv[1];
239 0 : sd_bus *bus = userdata;
240 : int r;
241 :
242 0 : if (!arg_pretty && !arg_static && !arg_transient)
243 0 : arg_pretty = arg_static = arg_transient = true;
244 :
245 0 : if (arg_pretty) {
246 : const char *p;
247 :
248 : /* If the passed hostname is already valid, then assume the user doesn't know anything about pretty
249 : * hostnames, so let's unset the pretty hostname, and just set the passed hostname as static/dynamic
250 : * hostname. */
251 0 : if (arg_static && hostname_is_valid(hostname, true))
252 0 : p = ""; /* No pretty hostname (as it is redundant), just a static one */
253 : else
254 0 : p = hostname; /* Use the passed name as pretty hostname */
255 :
256 0 : r = set_simple_string(bus, "SetPrettyHostname", p);
257 0 : if (r < 0)
258 0 : return r;
259 :
260 : /* Now that we set the pretty hostname, let's clean up the parameter and use that as static
261 : * hostname. If the hostname was already valid as static hostname, this will only chop off the trailing
262 : * dot if there is one. If it was not valid, then it will be made fully valid by truncating, dropping
263 : * multiple dots, and dropping weird chars. Note that we clean the name up only if we also are
264 : * supposed to set the pretty name. If the pretty name is not being set we assume the user knows what
265 : * he does and pass the name as-is. */
266 0 : h = strdup(hostname);
267 0 : if (!h)
268 0 : return log_oom();
269 :
270 0 : hostname = hostname_cleanup(h); /* Use the cleaned up name as static hostname */
271 : }
272 :
273 0 : if (arg_static) {
274 0 : r = set_simple_string(bus, "SetStaticHostname", hostname);
275 0 : if (r < 0)
276 0 : return r;
277 : }
278 :
279 0 : if (arg_transient) {
280 0 : r = set_simple_string(bus, "SetHostname", hostname);
281 0 : if (r < 0)
282 0 : return r;
283 : }
284 :
285 0 : return 0;
286 : }
287 :
288 0 : static int set_icon_name(int argc, char **argv, void *userdata) {
289 0 : return set_simple_string(userdata, "SetIconName", argv[1]);
290 : }
291 :
292 0 : static int set_chassis(int argc, char **argv, void *userdata) {
293 0 : return set_simple_string(userdata, "SetChassis", argv[1]);
294 : }
295 :
296 0 : static int set_deployment(int argc, char **argv, void *userdata) {
297 0 : return set_simple_string(userdata, "SetDeployment", argv[1]);
298 : }
299 :
300 0 : static int set_location(int argc, char **argv, void *userdata) {
301 0 : return set_simple_string(userdata, "SetLocation", argv[1]);
302 : }
303 :
304 3 : static int help(void) {
305 3 : _cleanup_free_ char *link = NULL;
306 : int r;
307 :
308 3 : r = terminal_urlify_man("hostnamectl", "1", &link);
309 3 : if (r < 0)
310 0 : return log_oom();
311 :
312 3 : printf("%s [OPTIONS...] COMMAND ...\n\n"
313 : "Query or change system hostname.\n\n"
314 : " -h --help Show this help\n"
315 : " --version Show package version\n"
316 : " --no-ask-password Do not prompt for password\n"
317 : " -H --host=[USER@]HOST Operate on remote host\n"
318 : " -M --machine=CONTAINER Operate on local container\n"
319 : " --transient Only set transient hostname\n"
320 : " --static Only set static hostname\n"
321 : " --pretty Only set pretty hostname\n\n"
322 : "Commands:\n"
323 : " status Show current hostname settings\n"
324 : " set-hostname NAME Set system hostname\n"
325 : " set-icon-name NAME Set icon name for host\n"
326 : " set-chassis NAME Set chassis type for host\n"
327 : " set-deployment NAME Set deployment environment for host\n"
328 : " set-location NAME Set location for host\n"
329 : "\nSee the %s for details.\n"
330 : , program_invocation_short_name
331 : , link
332 : );
333 :
334 3 : return 0;
335 : }
336 :
337 0 : static int verb_help(int argc, char **argv, void *userdata) {
338 0 : return help();
339 : }
340 :
341 4 : static int parse_argv(int argc, char *argv[]) {
342 :
343 : enum {
344 : ARG_VERSION = 0x100,
345 : ARG_NO_ASK_PASSWORD,
346 : ARG_TRANSIENT,
347 : ARG_STATIC,
348 : ARG_PRETTY
349 : };
350 :
351 : static const struct option options[] = {
352 : { "help", no_argument, NULL, 'h' },
353 : { "version", no_argument, NULL, ARG_VERSION },
354 : { "transient", no_argument, NULL, ARG_TRANSIENT },
355 : { "static", no_argument, NULL, ARG_STATIC },
356 : { "pretty", no_argument, NULL, ARG_PRETTY },
357 : { "host", required_argument, NULL, 'H' },
358 : { "machine", required_argument, NULL, 'M' },
359 : { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
360 : {}
361 : };
362 :
363 : int c;
364 :
365 4 : assert(argc >= 0);
366 4 : assert(argv);
367 :
368 4 : while ((c = getopt_long(argc, argv, "hH:M:", options, NULL)) >= 0)
369 :
370 4 : switch (c) {
371 :
372 3 : case 'h':
373 3 : return help();
374 :
375 0 : case ARG_VERSION:
376 0 : return version();
377 :
378 0 : case 'H':
379 0 : arg_transport = BUS_TRANSPORT_REMOTE;
380 0 : arg_host = optarg;
381 0 : break;
382 :
383 0 : case 'M':
384 0 : arg_transport = BUS_TRANSPORT_MACHINE;
385 0 : arg_host = optarg;
386 0 : break;
387 :
388 0 : case ARG_TRANSIENT:
389 0 : arg_transient = true;
390 0 : break;
391 :
392 0 : case ARG_PRETTY:
393 0 : arg_pretty = true;
394 0 : break;
395 :
396 0 : case ARG_STATIC:
397 0 : arg_static = true;
398 0 : break;
399 :
400 0 : case ARG_NO_ASK_PASSWORD:
401 0 : arg_ask_password = false;
402 0 : break;
403 :
404 1 : case '?':
405 1 : return -EINVAL;
406 :
407 0 : default:
408 0 : assert_not_reached("Unhandled option");
409 : }
410 :
411 0 : return 1;
412 : }
413 :
414 0 : static int hostnamectl_main(sd_bus *bus, int argc, char *argv[]) {
415 :
416 : static const Verb verbs[] = {
417 : { "status", VERB_ANY, 1, VERB_DEFAULT, show_status },
418 : { "set-hostname", 2, 2, 0, set_hostname },
419 : { "set-icon-name", 2, 2, 0, set_icon_name },
420 : { "set-chassis", 2, 2, 0, set_chassis },
421 : { "set-deployment", 2, 2, 0, set_deployment },
422 : { "set-location", 2, 2, 0, set_location },
423 : { "help", VERB_ANY, VERB_ANY, 0, verb_help }, /* Not documented, but supported since it is created. */
424 : {}
425 : };
426 :
427 0 : return dispatch_verb(argc, argv, verbs, bus);
428 : }
429 :
430 4 : static int run(int argc, char *argv[]) {
431 4 : _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
432 : int r;
433 :
434 4 : setlocale(LC_ALL, "");
435 4 : log_show_color(true);
436 4 : log_parse_environment();
437 4 : log_open();
438 :
439 4 : r = parse_argv(argc, argv);
440 4 : if (r <= 0)
441 4 : return r;
442 :
443 0 : r = bus_connect_transport(arg_transport, arg_host, false, &bus);
444 0 : if (r < 0)
445 0 : return log_error_errno(r, "Failed to create bus connection: %m");
446 :
447 0 : return hostnamectl_main(bus, argc, argv);
448 : }
449 :
450 4 : DEFINE_MAIN_FUNCTION(run);
|