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