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