chiark / gitweb /
hostnamectl: read OS pretty_name and cpe_name from remote
[elogind.git] / src / hostname / hostnamectl.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2012 Lennart Poettering
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 of the License, or
11   (at your option) any later version.
12
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   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <stdlib.h>
23 #include <stdbool.h>
24 #include <unistd.h>
25 #include <getopt.h>
26 #include <locale.h>
27 #include <string.h>
28 #include <sys/timex.h>
29 #include <sys/utsname.h>
30
31 #include "sd-bus.h"
32
33 #include "bus-util.h"
34 #include "bus-error.h"
35 #include "util.h"
36 #include "spawn-polkit-agent.h"
37 #include "build.h"
38 #include "hwclock.h"
39 #include "strv.h"
40 #include "sd-id128.h"
41 #include "virt.h"
42 #include "architecture.h"
43 #include "fileio.h"
44
45 static bool arg_ask_password = true;
46 static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
47 static char *arg_host = NULL;
48 static bool arg_transient = false;
49 static bool arg_pretty = false;
50 static bool arg_static = false;
51
52 static void polkit_agent_open_if_enabled(void) {
53
54         /* Open the polkit agent as a child process if necessary */
55         if (!arg_ask_password)
56                 return;
57
58         if (arg_transport != BUS_TRANSPORT_LOCAL)
59                 return;
60
61         polkit_agent_open();
62 }
63
64 typedef struct StatusInfo {
65         char *hostname;
66         char *static_hostname;
67         char *pretty_hostname;
68         char *icon_name;
69         char *chassis;
70         char *os_pretty_name;
71         char *os_cpe_name;
72         char *virtualization;
73         char *architecture;
74 } StatusInfo;
75
76 static void print_status_info(StatusInfo *i) {
77         sd_id128_t mid = {}, bid = {};
78         int r;
79         struct utsname u;
80
81         assert(i);
82
83         printf("   Static hostname: %s\n", strna(i->static_hostname));
84
85         if (!isempty(i->pretty_hostname) &&
86             !streq_ptr(i->pretty_hostname, i->static_hostname))
87                 printf("   Pretty hostname: %s\n", i->pretty_hostname);
88
89         if (!isempty(i->hostname) &&
90             !streq_ptr(i->hostname, i->static_hostname))
91                 printf("Transient hostname: %s\n", i->hostname);
92
93         printf("         Icon name: %s\n"
94                "           Chassis: %s\n",
95                strna(i->icon_name),
96                strna(i->chassis));
97
98         r = sd_id128_get_machine(&mid);
99         if (r >= 0)
100                 printf("        Machine ID: " SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(mid));
101
102         r = sd_id128_get_boot(&bid);
103         if (r >= 0)
104                 printf("           Boot ID: " SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(bid));
105
106         if (!isempty(i->virtualization))
107                 printf("    Virtualization: %s\n", i->virtualization);
108
109         if (!isempty(i->os_pretty_name))
110                 printf("  Operating System: %s\n", i->os_pretty_name);
111
112         if (!isempty(i->os_cpe_name))
113                 printf("       CPE OS Name: %s\n", i->os_cpe_name);
114
115         assert_se(uname(&u) >= 0);
116         printf("            Kernel: %s %s\n", u.sysname, u.release);
117
118         if (!isempty(i->architecture))
119                 printf("      Architecture: %s\n", i->architecture);
120
121 }
122
123 static int show_one_name(sd_bus *bus, const char* attr) {
124         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
125         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
126         const char *s;
127         int r;
128
129         r = sd_bus_get_property(
130                         bus,
131                         "org.freedesktop.hostname1",
132                         "/org/freedesktop/hostname1",
133                         "org.freedesktop.hostname1",
134                         attr,
135                         &error, &reply, "s");
136         if (r < 0) {
137                 log_error("Could not get property: %s", bus_error_message(&error, -r));
138                 return r;
139         }
140
141         r = sd_bus_message_read(reply, "s", &s);
142         if (r < 0)
143                 return bus_log_parse_error(r);
144
145         printf("%s\n", s);
146
147         return 0;
148 }
149
150 static int show_all_names(sd_bus *bus) {
151         StatusInfo info = {};
152
153         static const struct bus_properties_map hostname_map[]  = {
154                 { "Hostname",       "s", NULL, offsetof(StatusInfo, hostname) },
155                 { "StaticHostname", "s", NULL, offsetof(StatusInfo, static_hostname) },
156                 { "PrettyHostname", "s", NULL, offsetof(StatusInfo, pretty_hostname) },
157                 { "IconName",       "s", NULL, offsetof(StatusInfo, icon_name) },
158                 { "Chassis",        "s", NULL, offsetof(StatusInfo, chassis) },
159                 { "OperatingSystemPrettyName",     "s", NULL, offsetof(StatusInfo, os_pretty_name) },
160                 { "OperatingSystemCPEName",        "s", NULL, offsetof(StatusInfo, os_cpe_name) },
161                 {}
162         };
163
164         static const struct bus_properties_map manager_map[] = {
165                 { "Virtualization", "s", NULL, offsetof(StatusInfo, virtualization) },
166                 { "Architecture",   "s", NULL, offsetof(StatusInfo, architecture) },
167                 {}
168         };
169
170         int r;
171
172         r = bus_map_all_properties(bus,
173                                    "org.freedesktop.hostname1",
174                                    "/org/freedesktop/hostname1",
175                                    hostname_map,
176                                    &info);
177         if (r < 0)
178                 goto fail;
179
180         bus_map_all_properties(bus,
181                                "org.freedesktop.systemd1",
182                                "/org/freedesktop/systemd1",
183                                manager_map,
184                                &info);
185
186         print_status_info(&info);
187
188 fail:
189         free(info.hostname);
190         free(info.static_hostname);
191         free(info.pretty_hostname);
192         free(info.icon_name);
193         free(info.chassis);
194         free(info.os_pretty_name);
195         free(info.os_cpe_name);
196         free(info.virtualization);
197         free(info.architecture);
198
199         return r;
200 }
201
202 static int show_status(sd_bus *bus, char **args, unsigned n) {
203         assert(args);
204
205         if (arg_pretty || arg_static || arg_transient) {
206                 const char *attr;
207
208                 if (!!arg_static + !!arg_pretty + !!arg_transient > 1) {
209                         log_error("Cannot query more than one name type at a time");
210                         return -EINVAL;
211                 }
212
213                 attr = arg_pretty ? "PrettyHostname" :
214                         arg_static ? "StaticHostname" : "Hostname";
215
216                 return show_one_name(bus, attr);
217         } else
218                 return show_all_names(bus);
219 }
220
221 static int set_simple_string(sd_bus *bus, const char *method, const char *value) {
222         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
223         int r = 0;
224
225         polkit_agent_open_if_enabled();
226
227         r = sd_bus_call_method(
228                         bus,
229                         "org.freedesktop.hostname1",
230                         "/org/freedesktop/hostname1",
231                         "org.freedesktop.hostname1",
232                         method,
233                         &error, NULL,
234                         "sb", value, arg_ask_password);
235         if (r < 0)
236                 log_error("Could not set property: %s", bus_error_message(&error, -r));
237         return r;
238 }
239
240 static int set_hostname(sd_bus *bus, char **args, unsigned n) {
241         _cleanup_free_ char *h = NULL;
242         const char *hostname = args[1];
243         int r;
244
245         assert(args);
246         assert(n == 2);
247
248         if (!arg_pretty && !arg_static && !arg_transient)
249                 arg_pretty = arg_static = arg_transient = true;
250
251         if (arg_pretty) {
252                 const char *p;
253
254                 /* If the passed hostname is already valid, then
255                  * assume the user doesn't know anything about pretty
256                  * hostnames, so let's unset the pretty hostname, and
257                  * just set the passed hostname as static/dynamic
258                  * hostname. */
259
260                 h = strdup(hostname);
261                 if (!h)
262                         return log_oom();
263
264                 hostname_cleanup(h, true);
265
266                 if (arg_static && streq(h, hostname))
267                         p = "";
268                 else {
269                         p = hostname;
270                         hostname = h;
271                 }
272
273                 r = set_simple_string(bus, "SetPrettyHostname", p);
274                 if (r < 0)
275                         return r;
276         }
277
278         if (arg_static) {
279                 r = set_simple_string(bus, "SetStaticHostname", hostname);
280                 if (r < 0)
281                         return r;
282         }
283
284         if (arg_transient) {
285                 r = set_simple_string(bus, "SetHostname", hostname);
286                 if (r < 0)
287                         return r;
288         }
289
290         return 0;
291 }
292
293 static int set_icon_name(sd_bus *bus, char **args, unsigned n) {
294         assert(args);
295         assert(n == 2);
296
297         return set_simple_string(bus, "SetIconName", args[1]);
298 }
299
300 static int set_chassis(sd_bus *bus, char **args, unsigned n) {
301         assert(args);
302         assert(n == 2);
303
304         return set_simple_string(bus, "SetChassis", args[1]);
305 }
306
307 static int help(void) {
308
309         printf("%s [OPTIONS...] COMMAND ...\n\n"
310                "Query or change system hostname.\n\n"
311                "  -h --help              Show this help\n"
312                "     --version           Show package version\n"
313                "     --no-ask-password   Do not prompt for password\n"
314                "  -H --host=[USER@]HOST  Operate on remote host\n"
315                "  -M --machine=CONTAINER Operate on local container\n"
316                "     --transient         Only set transient hostname\n"
317                "     --static            Only set static hostname\n"
318                "     --pretty            Only set pretty hostname\n\n"
319                "Commands:\n"
320                "  status                 Show current hostname settings\n"
321                "  set-hostname NAME      Set system hostname\n"
322                "  set-icon-name NAME     Set icon name for host\n"
323                "  set-chassis NAME       Set chassis type for host\n",
324                program_invocation_short_name);
325
326         return 0;
327 }
328
329 static int parse_argv(int argc, char *argv[]) {
330
331         enum {
332                 ARG_VERSION = 0x100,
333                 ARG_NO_ASK_PASSWORD,
334                 ARG_TRANSIENT,
335                 ARG_STATIC,
336                 ARG_PRETTY
337         };
338
339         static const struct option options[] = {
340                 { "help",            no_argument,       NULL, 'h'                 },
341                 { "version",         no_argument,       NULL, ARG_VERSION         },
342                 { "transient",       no_argument,       NULL, ARG_TRANSIENT       },
343                 { "static",          no_argument,       NULL, ARG_STATIC          },
344                 { "pretty",          no_argument,       NULL, ARG_PRETTY          },
345                 { "host",            required_argument, NULL, 'H'                 },
346                 { "machine",         required_argument, NULL, 'M'                 },
347                 { "no-ask-password", no_argument,       NULL, ARG_NO_ASK_PASSWORD },
348                 {}
349         };
350
351         int c;
352
353         assert(argc >= 0);
354         assert(argv);
355
356         while ((c = getopt_long(argc, argv, "hH:M:", options, NULL)) >= 0) {
357
358                 switch (c) {
359
360                 case 'h':
361                         return help();
362
363                 case ARG_VERSION:
364                         puts(PACKAGE_STRING);
365                         puts(SYSTEMD_FEATURES);
366                         return 0;
367
368                 case 'H':
369                         arg_transport = BUS_TRANSPORT_REMOTE;
370                         arg_host = optarg;
371                         break;
372
373                 case 'M':
374                         arg_transport = BUS_TRANSPORT_CONTAINER;
375                         arg_host = optarg;
376                         break;
377
378                 case ARG_TRANSIENT:
379                         arg_transient = true;
380                         break;
381
382                 case ARG_PRETTY:
383                         arg_pretty = true;
384                         break;
385
386                 case ARG_STATIC:
387                         arg_static = true;
388                         break;
389
390                 case ARG_NO_ASK_PASSWORD:
391                         arg_ask_password = false;
392                         break;
393
394                 case '?':
395                         return -EINVAL;
396
397                 default:
398                         assert_not_reached("Unhandled option");
399                 }
400         }
401
402         return 1;
403 }
404
405 static int hostnamectl_main(sd_bus *bus, int argc, char *argv[]) {
406
407         static const struct {
408                 const char* verb;
409                 const enum {
410                         MORE,
411                         LESS,
412                         EQUAL
413                 } argc_cmp;
414                 const int argc;
415                 int (* const dispatch)(sd_bus *bus, char **args, unsigned n);
416         } verbs[] = {
417                 { "status",        LESS,  1, show_status   },
418                 { "set-hostname",  EQUAL, 2, set_hostname  },
419                 { "set-icon-name", EQUAL, 2, set_icon_name },
420                 { "set-chassis",   EQUAL, 2, set_chassis   },
421         };
422
423         int left;
424         unsigned i;
425
426         assert(argc >= 0);
427         assert(argv);
428
429         left = argc - optind;
430
431         if (left <= 0)
432                 /* Special rule: no arguments means "status" */
433                 i = 0;
434         else {
435                 if (streq(argv[optind], "help")) {
436                         help();
437                         return 0;
438                 }
439
440                 for (i = 0; i < ELEMENTSOF(verbs); i++)
441                         if (streq(argv[optind], verbs[i].verb))
442                                 break;
443
444                 if (i >= ELEMENTSOF(verbs)) {
445                         log_error("Unknown operation %s", argv[optind]);
446                         return -EINVAL;
447                 }
448         }
449
450         switch (verbs[i].argc_cmp) {
451
452         case EQUAL:
453                 if (left != verbs[i].argc) {
454                         log_error("Invalid number of arguments.");
455                         return -EINVAL;
456                 }
457
458                 break;
459
460         case MORE:
461                 if (left < verbs[i].argc) {
462                         log_error("Too few arguments.");
463                         return -EINVAL;
464                 }
465
466                 break;
467
468         case LESS:
469                 if (left > verbs[i].argc) {
470                         log_error("Too many arguments.");
471                         return -EINVAL;
472                 }
473
474                 break;
475
476         default:
477                 assert_not_reached("Unknown comparison operator.");
478         }
479
480         return verbs[i].dispatch(bus, argv + optind, left);
481 }
482
483 int main(int argc, char *argv[]) {
484         _cleanup_bus_unref_ sd_bus *bus = NULL;
485         int r;
486
487         setlocale(LC_ALL, "");
488         log_parse_environment();
489         log_open();
490
491         r = parse_argv(argc, argv);
492         if (r <= 0)
493                 goto finish;
494
495         r = bus_open_transport(arg_transport, arg_host, false, &bus);
496         if (r < 0) {
497                 log_error("Failed to create bus connection: %s", strerror(-r));
498                 goto finish;
499         }
500
501         r = hostnamectl_main(bus, argc, argv);
502
503 finish:
504         return r < 0 ? EXIT_FAILURE : r;
505 }