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