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