Branch data 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 : 12 : static int help(void) {
305 : 12 : _cleanup_free_ char *link = NULL;
306 : : int r;
307 : :
308 : 12 : r = terminal_urlify_man("hostnamectl", "1", &link);
309 [ - + ]: 12 : if (r < 0)
310 : 0 : return log_oom();
311 : :
312 : 12 : 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 : 12 : return 0;
335 : : }
336 : :
337 : 0 : static int verb_help(int argc, char **argv, void *userdata) {
338 : 0 : return help();
339 : : }
340 : :
341 : 16 : 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 [ - + ]: 16 : assert(argc >= 0);
366 [ - + ]: 16 : assert(argv);
367 : :
368 [ + - ]: 16 : while ((c = getopt_long(argc, argv, "hH:M:", options, NULL)) >= 0)
369 : :
370 [ + - - - : 16 : switch (c) {
- - - - +
- ]
371 : :
372 : 12 : case 'h':
373 : 12 : 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 : 4 : case '?':
405 : 4 : 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 : 16 : static int run(int argc, char *argv[]) {
431 : 16 : _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
432 : : int r;
433 : :
434 : 16 : setlocale(LC_ALL, "");
435 : 16 : log_show_color(true);
436 : 16 : log_parse_environment();
437 : 16 : log_open();
438 : :
439 : 16 : r = parse_argv(argc, argv);
440 [ + - ]: 16 : if (r <= 0)
441 : 16 : 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 : 16 : DEFINE_MAIN_FUNCTION(run);
|