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