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