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