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