chiark / gitweb /
clients: various simplifications
[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                 { NULL,              0,                 NULL, 0                   }
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                         help();
337                         return 0;
338
339                 case ARG_VERSION:
340                         puts(PACKAGE_STRING);
341                         puts(SYSTEMD_FEATURES);
342                         return 0;
343
344                 case 'H':
345                         arg_transport = BUS_TRANSPORT_REMOTE;
346                         arg_host = optarg;
347                         break;
348
349                 case 'M':
350                         arg_transport = BUS_TRANSPORT_CONTAINER;
351                         arg_host = optarg;
352                         break;
353
354                 case ARG_TRANSIENT:
355                         arg_transient = true;
356                         break;
357
358                 case ARG_PRETTY:
359                         arg_pretty = true;
360                         break;
361
362                 case ARG_STATIC:
363                         arg_static = true;
364                         break;
365
366                 case ARG_NO_ASK_PASSWORD:
367                         arg_ask_password = false;
368                         break;
369
370                 case '?':
371                         return -EINVAL;
372
373                 default:
374                         log_error("Unknown option code %c", c);
375                         return -EINVAL;
376                 }
377         }
378
379         return 1;
380 }
381
382 static int hostnamectl_main(sd_bus *bus, int argc, char *argv[]) {
383
384         static const struct {
385                 const char* verb;
386                 const enum {
387                         MORE,
388                         LESS,
389                         EQUAL
390                 } argc_cmp;
391                 const int argc;
392                 int (* const dispatch)(sd_bus *bus, char **args, unsigned n);
393         } verbs[] = {
394                 { "status",        LESS,  1, show_status   },
395                 { "set-hostname",  EQUAL, 2, set_hostname  },
396                 { "set-icon-name", EQUAL, 2, set_icon_name },
397                 { "set-chassis",   EQUAL, 2, set_chassis   },
398         };
399
400         int left;
401         unsigned i;
402
403         assert(argc >= 0);
404         assert(argv);
405
406         left = argc - optind;
407
408         if (left <= 0)
409                 /* Special rule: no arguments means "status" */
410                 i = 0;
411         else {
412                 if (streq(argv[optind], "help")) {
413                         help();
414                         return 0;
415                 }
416
417                 for (i = 0; i < ELEMENTSOF(verbs); i++)
418                         if (streq(argv[optind], verbs[i].verb))
419                                 break;
420
421                 if (i >= ELEMENTSOF(verbs)) {
422                         log_error("Unknown operation %s", argv[optind]);
423                         return -EINVAL;
424                 }
425         }
426
427         switch (verbs[i].argc_cmp) {
428
429         case EQUAL:
430                 if (left != verbs[i].argc) {
431                         log_error("Invalid number of arguments.");
432                         return -EINVAL;
433                 }
434
435                 break;
436
437         case MORE:
438                 if (left < verbs[i].argc) {
439                         log_error("Too few arguments.");
440                         return -EINVAL;
441                 }
442
443                 break;
444
445         case LESS:
446                 if (left > verbs[i].argc) {
447                         log_error("Too many arguments.");
448                         return -EINVAL;
449                 }
450
451                 break;
452
453         default:
454                 assert_not_reached("Unknown comparison operator.");
455         }
456
457         return verbs[i].dispatch(bus, argv + optind, left);
458 }
459
460 int main(int argc, char *argv[]) {
461         _cleanup_bus_unref_ sd_bus *bus = NULL;
462         int r;
463
464         setlocale(LC_ALL, "");
465         log_parse_environment();
466         log_open();
467
468         r = parse_argv(argc, argv);
469         if (r <= 0)
470                 goto finish;
471
472         r = bus_open_transport(arg_transport, arg_host, false, &bus);
473         if (r < 0) {
474                 log_error("Failed to create bus connection: %s", strerror(-r));
475                 goto finish;
476         }
477
478         r = hostnamectl_main(bus, argc, argv);
479
480 finish:
481         return r < 0 ? EXIT_FAILURE : r;
482 }