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