chiark / gitweb /
Fix --no-ask-password
[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 "dbus-common.h"
32 #include "util.h"
33 #include "spawn-polkit-agent.h"
34 #include "build.h"
35 #include "hwclock.h"
36 #include "strv.h"
37 #include "sd-id128.h"
38 #include "virt.h"
39 #include "fileio.h"
40
41 static enum transport {
42         TRANSPORT_NORMAL,
43         TRANSPORT_SSH,
44         TRANSPORT_POLKIT
45 } arg_transport = TRANSPORT_NORMAL;
46 static bool arg_ask_password = true;
47 static const char *arg_host = NULL;
48 static bool arg_set_transient = false;
49 static bool arg_set_pretty = false;
50 static bool arg_set_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
56         if (!arg_ask_password)
57                 return;
58
59         polkit_agent_open();
60 }
61
62 typedef struct StatusInfo {
63         const char *hostname;
64         const char *static_hostname;
65         const char *pretty_hostname;
66         const char *icon_name;
67         const char *chassis;
68 } StatusInfo;
69
70 static void print_status_info(StatusInfo *i) {
71         sd_id128_t mid, bid;
72         int r;
73         const char *id = NULL;
74         _cleanup_free_ char *pretty_name = NULL, *cpe_name = NULL;
75         struct utsname u;
76
77         assert(i);
78
79         printf("   Static hostname: %s\n",
80                strna(i->static_hostname));
81
82         if (!isempty(i->pretty_hostname) &&
83             !streq_ptr(i->pretty_hostname, i->static_hostname))
84                 printf("   Pretty hostname: %s\n",
85                        strna(i->pretty_hostname));
86
87         if (!isempty(i->hostname) &&
88             !streq_ptr(i->hostname, i->static_hostname))
89                 printf("Transient hostname: %s\n",
90                        strna(i->hostname));
91
92         printf("         Icon name: %s\n"
93                "           Chassis: %s\n",
94                strna(i->icon_name),
95                strna(i->chassis));
96
97         r = sd_id128_get_machine(&mid);
98         if (r >= 0)
99                 printf("        Machine ID: " SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(mid));
100
101         r = sd_id128_get_boot(&bid);
102         if (r >= 0)
103                 printf("           Boot ID: " SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(bid));
104
105         if (detect_virtualization(&id) > 0)
106                 printf("    Virtualization: %s\n", id);
107
108         r = parse_env_file("/etc/os-release", NEWLINE,
109                            "PRETTY_NAME", &pretty_name,
110                            "CPE_NAME", &cpe_name,
111                            NULL);
112
113         if (!isempty(pretty_name))
114                 printf("  Operating System: %s\n", pretty_name);
115
116         if (!isempty(cpe_name))
117                 printf("       CPE OS Name: %s\n", cpe_name);
118
119         assert_se(uname(&u) >= 0);
120         printf("            Kernel: %s %s\n"
121                "      Architecture: %s\n", u.sysname, u.release, u.machine);
122
123 }
124
125 static int status_property(const char *name, DBusMessageIter *iter, StatusInfo *i) {
126         assert(name);
127         assert(iter);
128
129         switch (dbus_message_iter_get_arg_type(iter)) {
130
131         case DBUS_TYPE_STRING: {
132                 const char *s;
133
134                 dbus_message_iter_get_basic(iter, &s);
135                 if (!isempty(s)) {
136                         if (streq(name, "Hostname"))
137                                 i->hostname = s;
138                         if (streq(name, "StaticHostname"))
139                                 i->static_hostname = s;
140                         if (streq(name, "PrettyHostname"))
141                                 i->pretty_hostname = s;
142                         if (streq(name, "IconName"))
143                                 i->icon_name = s;
144                         if (streq(name, "Chassis"))
145                                 i->chassis = s;
146                 }
147                 break;
148         }
149         }
150
151         return 0;
152 }
153
154 static int show_status(DBusConnection *bus, char **args, unsigned n) {
155         _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
156         const char *interface = "";
157         int r;
158         DBusMessageIter iter, sub, sub2, sub3;
159         StatusInfo info = {};
160
161         assert(args);
162
163         r = bus_method_call_with_reply(
164                         bus,
165                         "org.freedesktop.hostname1",
166                         "/org/freedesktop/hostname1",
167                         "org.freedesktop.DBus.Properties",
168                         "GetAll",
169                         &reply,
170                         NULL,
171                         DBUS_TYPE_STRING, &interface,
172                         DBUS_TYPE_INVALID);
173         if (r < 0)
174                 return r;
175
176         if (!dbus_message_iter_init(reply, &iter) ||
177             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
178             dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY)  {
179                 log_error("Failed to parse reply.");
180                 return -EIO;
181         }
182
183         dbus_message_iter_recurse(&iter, &sub);
184
185         while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
186                 const char *name;
187
188                 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_DICT_ENTRY) {
189                         log_error("Failed to parse reply.");
190                         return -EIO;
191                 }
192
193                 dbus_message_iter_recurse(&sub, &sub2);
194
195                 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0) {
196                         log_error("Failed to parse reply.");
197                         return -EIO;
198                 }
199
200                 if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT)  {
201                         log_error("Failed to parse reply.");
202                         return -EIO;
203                 }
204
205                 dbus_message_iter_recurse(&sub2, &sub3);
206
207                 r = status_property(name, &sub3, &info);
208                 if (r < 0) {
209                         log_error("Failed to parse reply.");
210                         return r;
211                 }
212
213                 dbus_message_iter_next(&sub);
214         }
215
216         print_status_info(&info);
217         return 0;
218 }
219
220 static int set_hostname(DBusConnection *bus, char **args, unsigned n) {
221         _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
222         dbus_bool_t interactive = arg_ask_password;
223         _cleanup_free_ char *h = NULL;
224         const char *hostname = args[1];
225         int r;
226
227         assert(args);
228         assert(n == 2);
229
230         polkit_agent_open_if_enabled();
231
232         if (arg_set_pretty) {
233                 const char *p;
234
235                 /* If the passed hostname is already valid, then
236                  * assume the user doesn't know anything about pretty
237                  * hostnames, so let's unset the pretty hostname, and
238                  * just set the passed hostname as static/dynamic
239                  * hostname. */
240
241                 h = strdup(hostname);
242                 if (!h)
243                         return log_oom();
244
245                 hostname_cleanup(h, true);
246
247                 if (arg_set_static && streq(h, hostname))
248                         p = "";
249                 else {
250                         p = hostname;
251                         hostname = h;
252                 }
253
254                 r = bus_method_call_with_reply(
255                                 bus,
256                                 "org.freedesktop.hostname1",
257                                 "/org/freedesktop/hostname1",
258                                 "org.freedesktop.hostname1",
259                                 "SetPrettyHostname",
260                                 &reply,
261                                 NULL,
262                                 DBUS_TYPE_STRING, &p,
263                                 DBUS_TYPE_BOOLEAN, &interactive,
264                                 DBUS_TYPE_INVALID);
265                 if (r < 0)
266                         return r;
267
268                 dbus_message_unref(reply);
269                 reply = NULL;
270         }
271
272         if (arg_set_static) {
273                 r = bus_method_call_with_reply(
274                                 bus,
275                                 "org.freedesktop.hostname1",
276                                 "/org/freedesktop/hostname1",
277                                 "org.freedesktop.hostname1",
278                                 "SetStaticHostname",
279                                 &reply,
280                                 NULL,
281                                 DBUS_TYPE_STRING, &hostname,
282                                 DBUS_TYPE_BOOLEAN, &interactive,
283                                 DBUS_TYPE_INVALID);
284
285                 if (r < 0)
286                         return r;
287
288                 dbus_message_unref(reply);
289                 reply = NULL;
290         }
291
292         if (arg_set_transient) {
293                 r = bus_method_call_with_reply(
294                                 bus,
295                                 "org.freedesktop.hostname1",
296                                 "/org/freedesktop/hostname1",
297                                 "org.freedesktop.hostname1",
298                                 "SetHostname",
299                                 &reply,
300                                 NULL,
301                                 DBUS_TYPE_STRING, &hostname,
302                                 DBUS_TYPE_BOOLEAN, &interactive,
303                                 DBUS_TYPE_INVALID);
304
305                 if (r < 0)
306                         return r;
307         }
308
309         return 0;
310 }
311
312 static int set_icon_name(DBusConnection *bus, char **args, unsigned n) {
313         _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
314         dbus_bool_t interactive = arg_ask_password;
315
316         assert(args);
317         assert(n == 2);
318
319         polkit_agent_open_if_enabled();
320
321         return bus_method_call_with_reply(
322                         bus,
323                         "org.freedesktop.hostname1",
324                         "/org/freedesktop/hostname1",
325                         "org.freedesktop.hostname1",
326                         "SetIconName",
327                         &reply,
328                         NULL,
329                         DBUS_TYPE_STRING, &args[1],
330                         DBUS_TYPE_BOOLEAN, &interactive,
331                         DBUS_TYPE_INVALID);
332 }
333
334 static int set_chassis(DBusConnection *bus, char **args, unsigned n) {
335         _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
336         dbus_bool_t interactive = arg_ask_password;
337
338         assert(args);
339         assert(n == 2);
340
341         polkit_agent_open_if_enabled();
342
343         return bus_method_call_with_reply(
344                         bus,
345                         "org.freedesktop.hostname1",
346                         "/org/freedesktop/hostname1",
347                         "org.freedesktop.hostname1",
348                         "SetChassis",
349                         &reply,
350                         NULL,
351                         DBUS_TYPE_STRING, &args[1],
352                         DBUS_TYPE_BOOLEAN, &interactive,
353                         DBUS_TYPE_INVALID);
354 }
355
356 static int help(void) {
357
358         printf("%s [OPTIONS...] COMMAND ...\n\n"
359                "Query or change system hostname.\n\n"
360                "  -h --help              Show this help\n"
361                "     --version           Show package version\n"
362                "     --transient         Only set transient hostname\n"
363                "     --static            Only set static hostname\n"
364                "     --pretty            Only set pretty hostname\n"
365                "  -P --privileged        Acquire privileges before execution\n"
366                "     --no-ask-password   Do not prompt for password\n"
367                "  -H --host=[USER@]HOST  Operate on remote host\n\n"
368                "Commands:\n"
369                "  status                 Show current hostname settings\n"
370                "  set-hostname NAME      Set system hostname\n"
371                "  set-icon-name NAME     Set icon name for host\n"
372                "  set-chassis NAME       Set chassis type for host\n",
373                program_invocation_short_name);
374
375         return 0;
376 }
377
378 static int parse_argv(int argc, char *argv[]) {
379
380         enum {
381                 ARG_VERSION = 0x100,
382                 ARG_NO_ASK_PASSWORD,
383                 ARG_SET_TRANSIENT,
384                 ARG_SET_STATIC,
385                 ARG_SET_PRETTY
386         };
387
388         static const struct option options[] = {
389                 { "help",            no_argument,       NULL, 'h'                 },
390                 { "version",         no_argument,       NULL, ARG_VERSION         },
391                 { "transient",       no_argument,       NULL, ARG_SET_TRANSIENT   },
392                 { "static",          no_argument,       NULL, ARG_SET_STATIC      },
393                 { "pretty",          no_argument,       NULL, ARG_SET_PRETTY      },
394                 { "host",            required_argument, NULL, 'H'                 },
395                 { "privileged",      no_argument,       NULL, 'P'                 },
396                 { "no-ask-password", no_argument,       NULL, ARG_NO_ASK_PASSWORD },
397                 { NULL,              0,                 NULL, 0                   }
398         };
399
400         int c;
401
402         assert(argc >= 0);
403         assert(argv);
404
405         while ((c = getopt_long(argc, argv, "hH:P", options, NULL)) >= 0) {
406
407                 switch (c) {
408
409                 case 'h':
410                         help();
411                         return 0;
412
413                 case ARG_VERSION:
414                         puts(PACKAGE_STRING);
415                         puts(SYSTEMD_FEATURES);
416                         return 0;
417
418                 case 'P':
419                         arg_transport = TRANSPORT_POLKIT;
420                         break;
421
422                 case 'H':
423                         arg_transport = TRANSPORT_SSH;
424                         arg_host = optarg;
425                         break;
426
427                 case ARG_SET_TRANSIENT:
428                         arg_set_transient = true;
429                         break;
430
431                 case ARG_SET_PRETTY:
432                         arg_set_pretty = true;
433                         break;
434
435                 case ARG_SET_STATIC:
436                         arg_set_static = true;
437                         break;
438
439                 case ARG_NO_ASK_PASSWORD:
440                         arg_ask_password = false;
441                         break;
442
443                 case '?':
444                         return -EINVAL;
445
446                 default:
447                         log_error("Unknown option code %c", c);
448                         return -EINVAL;
449                 }
450         }
451
452         if (!arg_set_transient && !arg_set_pretty && !arg_set_static)
453                 arg_set_transient = arg_set_pretty = arg_set_static = true;
454
455         return 1;
456 }
457
458 static int hostnamectl_main(DBusConnection *bus, int argc, char *argv[], DBusError *error) {
459
460         static const struct {
461                 const char* verb;
462                 const enum {
463                         MORE,
464                         LESS,
465                         EQUAL
466                 } argc_cmp;
467                 const int argc;
468                 int (* const dispatch)(DBusConnection *bus, char **args, unsigned n);
469         } verbs[] = {
470                 { "status",        LESS,  1, show_status   },
471                 { "set-hostname",  EQUAL, 2, set_hostname  },
472                 { "set-icon-name", EQUAL, 2, set_icon_name },
473                 { "set-chassis",   EQUAL, 2, set_chassis   },
474         };
475
476         int left;
477         unsigned i;
478
479         assert(argc >= 0);
480         assert(argv);
481         assert(error);
482
483         left = argc - optind;
484
485         if (left <= 0)
486                 /* Special rule: no arguments means "status" */
487                 i = 0;
488         else {
489                 if (streq(argv[optind], "help")) {
490                         help();
491                         return 0;
492                 }
493
494                 for (i = 0; i < ELEMENTSOF(verbs); i++)
495                         if (streq(argv[optind], verbs[i].verb))
496                                 break;
497
498                 if (i >= ELEMENTSOF(verbs)) {
499                         log_error("Unknown operation %s", argv[optind]);
500                         return -EINVAL;
501                 }
502         }
503
504         switch (verbs[i].argc_cmp) {
505
506         case EQUAL:
507                 if (left != verbs[i].argc) {
508                         log_error("Invalid number of arguments.");
509                         return -EINVAL;
510                 }
511
512                 break;
513
514         case MORE:
515                 if (left < verbs[i].argc) {
516                         log_error("Too few arguments.");
517                         return -EINVAL;
518                 }
519
520                 break;
521
522         case LESS:
523                 if (left > verbs[i].argc) {
524                         log_error("Too many arguments.");
525                         return -EINVAL;
526                 }
527
528                 break;
529
530         default:
531                 assert_not_reached("Unknown comparison operator.");
532         }
533
534         if (!bus) {
535                 log_error("Failed to get D-Bus connection: %s", error->message);
536                 return -EIO;
537         }
538
539         return verbs[i].dispatch(bus, argv + optind, left);
540 }
541
542 int main(int argc, char *argv[]) {
543         int r, retval = EXIT_FAILURE;
544         DBusConnection *bus = NULL;
545         DBusError error;
546
547         dbus_error_init(&error);
548
549         setlocale(LC_ALL, "");
550         log_parse_environment();
551         log_open();
552
553         r = parse_argv(argc, argv);
554         if (r < 0)
555                 goto finish;
556         else if (r == 0) {
557                 retval = EXIT_SUCCESS;
558                 goto finish;
559         }
560
561         if (arg_transport == TRANSPORT_NORMAL)
562                 bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
563         else if (arg_transport == TRANSPORT_POLKIT)
564                 bus_connect_system_polkit(&bus, &error);
565         else if (arg_transport == TRANSPORT_SSH)
566                 bus_connect_system_ssh(NULL, arg_host, &bus, &error);
567         else
568                 assert_not_reached("Uh, invalid transport...");
569
570         r = hostnamectl_main(bus, argc, argv, &error);
571         retval = r < 0 ? EXIT_FAILURE : r;
572
573 finish:
574         if (bus) {
575                 dbus_connection_flush(bus);
576                 dbus_connection_close(bus);
577                 dbus_connection_unref(bus);
578         }
579
580         dbus_error_free(&error);
581         dbus_shutdown();
582
583         return retval;
584 }