chiark / gitweb /
8d8dc3d7f357e73a24def4fe363f08f28c45d168
[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         const struct bus_properties_map map[]  = {
146                 { "s",  "Hostname",       &info.hostname },
147                 { "s",  "StaticHostname", &info.static_hostname },
148                 { "s",  "PrettyHostname", &info.pretty_hostname },
149                 { "s",  "IconName",       &info.icon_name },
150                 { "s",  "Chassis",        &info.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         if (r < 0)
160                 goto fail;
161
162         print_status_info(&info);
163
164 fail:
165         free(info.hostname);
166         free(info.static_hostname);
167         free(info.pretty_hostname);
168         free(info.icon_name);
169         free(info.chassis);
170         return 0;
171 }
172
173 static int show_status(sd_bus *bus, char **args, unsigned n) {
174         assert(args);
175
176         if (arg_pretty || arg_static || arg_transient) {
177                 const char *attr;
178
179                 if (!!arg_static + !!arg_pretty + !!arg_transient > 1) {
180                         log_error("Cannot query more than one name type at a time");
181                         return -EINVAL;
182                 }
183
184                 attr = arg_pretty ? "PrettyHostname" :
185                         arg_static ? "StaticHostname" : "Hostname";
186
187                 return show_one_name(bus, attr);
188         } else
189                 return show_all_names(bus);
190 }
191
192 static int set_simple_string(sd_bus *bus, const char *method, const char *value) {
193         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
194         static bool first=true;
195         int r = 0;
196
197         if (first && arg_ask_password)
198                 polkit_agent_open();
199         first = false;
200
201         r = sd_bus_call_method(
202                         bus,
203                         "org.freedesktop.hostname1",
204                         "/org/freedesktop/hostname1",
205                         "org.freedesktop.hostname1",
206                         method,
207                         &error, NULL,
208                         "sb", value, arg_ask_password);
209         if (r < 0)
210                 log_error("Could not set property: %s", bus_error_message(&error, -r));
211         return r;
212 }
213
214 static int set_hostname(sd_bus *bus, char **args, unsigned n) {
215         _cleanup_free_ char *h = NULL;
216         const char *hostname = args[1];
217         int r;
218
219         assert(args);
220         assert(n == 2);
221
222         if (!arg_pretty && !arg_static && !arg_transient)
223                 arg_pretty = arg_static = arg_transient = true;
224
225         if (arg_pretty) {
226                 const char *p;
227
228                 /* If the passed hostname is already valid, then
229                  * assume the user doesn't know anything about pretty
230                  * hostnames, so let's unset the pretty hostname, and
231                  * just set the passed hostname as static/dynamic
232                  * hostname. */
233
234                 h = strdup(hostname);
235                 if (!h)
236                         return log_oom();
237
238                 hostname_cleanup(h, true);
239
240                 if (arg_static && streq(h, hostname))
241                         p = "";
242                 else {
243                         p = hostname;
244                         hostname = h;
245                 }
246
247                 r = set_simple_string(bus, "SetPrettyHostname", p);
248                 if (r < 0)
249                         return r;
250         }
251
252         if (arg_static) {
253                 r = set_simple_string(bus, "SetStaticHostname", hostname);
254                 if (r < 0)
255                         return r;
256         }
257
258         if (arg_transient) {
259                 r = set_simple_string(bus, "SetHostname", hostname);
260                 if (r < 0)
261                         return r;
262         }
263
264         return 0;
265 }
266
267 static int set_icon_name(sd_bus *bus, char **args, unsigned n) {
268         assert(args);
269         assert(n == 2);
270
271         return set_simple_string(bus, "SetIconName", args[1]);
272 }
273
274 static int set_chassis(sd_bus *bus, char **args, unsigned n) {
275         assert(args);
276         assert(n == 2);
277
278         return set_simple_string(bus, "SetChasis", args[1]);
279 }
280
281 static int help(void) {
282
283         printf("%s [OPTIONS...] COMMAND ...\n\n"
284                "Query or change system hostname.\n\n"
285                "  -h --help              Show this help\n"
286                "     --version           Show package version\n"
287                "     --transient         Only set transient hostname\n"
288                "     --static            Only set static hostname\n"
289                "     --pretty            Only set pretty hostname\n"
290                "     --no-ask-password   Do not prompt for password\n"
291                "  -H --host=[USER@]HOST  Operate on remote host\n"
292                "  -M --machine=CONTAINER Operate on local container\n\n"
293                "Commands:\n"
294                "  status                 Show current hostname settings\n"
295                "  set-hostname NAME      Set system hostname\n"
296                "  set-icon-name NAME     Set icon name for host\n"
297                "  set-chassis NAME       Set chassis type for host\n",
298                program_invocation_short_name);
299
300         return 0;
301 }
302
303 static int parse_argv(int argc, char *argv[]) {
304
305         enum {
306                 ARG_VERSION = 0x100,
307                 ARG_NO_ASK_PASSWORD,
308                 ARG_TRANSIENT,
309                 ARG_STATIC,
310                 ARG_PRETTY
311         };
312
313         static const struct option options[] = {
314                 { "help",            no_argument,       NULL, 'h'                 },
315                 { "version",         no_argument,       NULL, ARG_VERSION         },
316                 { "transient",       no_argument,       NULL, ARG_TRANSIENT   },
317                 { "static",          no_argument,       NULL, ARG_STATIC      },
318                 { "pretty",          no_argument,       NULL, ARG_PRETTY      },
319                 { "host",            required_argument, NULL, 'H'                 },
320                 { "machine",         required_argument, NULL, 'M'                 },
321                 { "no-ask-password", no_argument,       NULL, ARG_NO_ASK_PASSWORD },
322                 { NULL,              0,                 NULL, 0                   }
323         };
324
325         int c;
326
327         assert(argc >= 0);
328         assert(argv);
329
330         while ((c = getopt_long(argc, argv, "hH:M:", options, NULL)) >= 0) {
331
332                 switch (c) {
333
334                 case 'h':
335                         help();
336                         return 0;
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                         log_error("Unknown option code %c", c);
374                         return -EINVAL;
375                 }
376         }
377
378         return 1;
379 }
380
381 static int hostnamectl_main(sd_bus *bus, int argc, char *argv[]) {
382
383         static const struct {
384                 const char* verb;
385                 const enum {
386                         MORE,
387                         LESS,
388                         EQUAL
389                 } argc_cmp;
390                 const int argc;
391                 int (* const dispatch)(sd_bus *bus, char **args, unsigned n);
392         } verbs[] = {
393                 { "status",        LESS,  1, show_status   },
394                 { "set-hostname",  EQUAL, 2, set_hostname  },
395                 { "set-icon-name", EQUAL, 2, set_icon_name },
396                 { "set-chassis",   EQUAL, 2, set_chassis   },
397         };
398
399         int left;
400         unsigned i;
401
402         assert(argc >= 0);
403         assert(argv);
404
405         left = argc - optind;
406
407         if (left <= 0)
408                 /* Special rule: no arguments means "status" */
409                 i = 0;
410         else {
411                 if (streq(argv[optind], "help")) {
412                         help();
413                         return 0;
414                 }
415
416                 for (i = 0; i < ELEMENTSOF(verbs); i++)
417                         if (streq(argv[optind], verbs[i].verb))
418                                 break;
419
420                 if (i >= ELEMENTSOF(verbs)) {
421                         log_error("Unknown operation %s", argv[optind]);
422                         return -EINVAL;
423                 }
424         }
425
426         switch (verbs[i].argc_cmp) {
427
428         case EQUAL:
429                 if (left != verbs[i].argc) {
430                         log_error("Invalid number of arguments.");
431                         return -EINVAL;
432                 }
433
434                 break;
435
436         case MORE:
437                 if (left < verbs[i].argc) {
438                         log_error("Too few arguments.");
439                         return -EINVAL;
440                 }
441
442                 break;
443
444         case LESS:
445                 if (left > verbs[i].argc) {
446                         log_error("Too many arguments.");
447                         return -EINVAL;
448                 }
449
450                 break;
451
452         default:
453                 assert_not_reached("Unknown comparison operator.");
454         }
455
456         return verbs[i].dispatch(bus, argv + optind, left);
457 }
458
459 int main(int argc, char *argv[]) {
460         int r;
461         _cleanup_bus_unref_ sd_bus *bus = NULL;
462
463         setlocale(LC_ALL, "");
464         log_parse_environment();
465         log_open();
466
467         r = parse_argv(argc, argv);
468         if (r <= 0)
469                 goto finish;
470
471         r = bus_open_transport(arg_transport, arg_host, false, &bus);
472         if (r < 0) {
473                 log_error("Failed to create bus connection: %s", strerror(-r));
474                 goto finish;
475         }
476
477         r = hostnamectl_main(bus, argc, argv);
478
479 finish:
480         return r < 0 ? EXIT_FAILURE : r;
481 }