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