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