chiark / gitweb /
d25ddef6e5ac7c61d99217b79ad8922060d5cf0c
[elogind.git] / src / boot / bootctl.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2013 Kay Sievers
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
30 #include "boot.h"
31 #include "build.h"
32 #include "util.h"
33 #include "utf8.h"
34
35 static int help(void) {
36         printf("%s [OPTIONS...] COMMAND ...\n\n"
37                "Query or change firmware and boot manager settings.\n\n"
38                "  -h --help              Show this help\n"
39                "     --version           Show package version\n"
40                "Commands:\n"
41                "  status                 Show current boot settings\n",
42                program_invocation_short_name);
43
44         return 0;
45 }
46
47 static int parse_argv(int argc, char *argv[]) {
48         enum {
49                 ARG_VERSION = 0x100,
50         };
51
52         static const struct option options[] = {
53                 { "help",                no_argument,       NULL, 'h'                     },
54                 { "version",             no_argument,       NULL, ARG_VERSION             },
55                 { NULL,                  0,                 NULL, 0                       }
56         };
57
58         int c;
59
60         assert(argc >= 0);
61         assert(argv);
62         while ((c = getopt_long(argc, argv, "+hH:P", options, NULL)) >= 0) {
63
64                 switch (c) {
65
66                 case 'h':
67                         help();
68                         return 0;
69
70                 case ARG_VERSION:
71                         puts(PACKAGE_STRING);
72                         puts(SYSTEMD_FEATURES);
73                         return 0;
74
75                 case '?':
76                         return -EINVAL;
77
78                 default:
79                         log_error("Unknown option code %c", c);
80                         return -EINVAL;
81                 }
82         }
83
84         return 1;
85 }
86
87 static int boot_info_new(struct boot_info **info) {
88         struct boot_info *in;
89         int err;
90
91         in = new0(struct boot_info, 1);
92         if (!in)
93                 return -ENOMEM;
94
95         err = sd_id128_get_machine(&in->machine_id);
96         if (err < 0)
97                 goto err;
98
99         err = sd_id128_get_boot(&in->boot_id);
100         if (err < 0)
101                 goto err;
102
103         in->fw_entry_active = -1;
104         in->loader_entry_active = -1;
105
106         *info = in;
107         return 0;
108 err:
109         free(in);
110         return err;
111 }
112
113 static void boot_info_entries_free(struct boot_info_entry *entries, size_t n) {
114         size_t i;
115
116         for (i = 0; i < n; i++) {
117                 free(entries[i].title);
118                 free(entries[i].path);
119         }
120         free(entries);
121 }
122
123 static void boot_info_free(struct boot_info *info) {
124         free(info->fw_type);
125         free(info->fw_info);
126         boot_info_entries_free(info->fw_entries, info->fw_entries_count);
127         free(info->fw_entries_order);
128         free(info->loader);
129         free(info->loader_image_path);
130         free(info->loader_options_added);
131         boot_info_entries_free(info->loader_entries, info->loader_entries_count);
132         free(info);
133 }
134
135 static int show_status(char **args, unsigned n) {
136         char buf[64];
137         struct boot_info *info;
138         int err;
139
140         err = boot_info_new(&info);
141         if (err < 0)
142                 return -ENOMEM;
143
144         err = boot_info_query(info);
145
146         printf("System:\n");
147         printf("   Machine ID: %s\n", sd_id128_to_string(info->machine_id, buf));
148         printf("      Boot ID: %s\n", sd_id128_to_string(info->boot_id, buf));
149         if (info->fw_type)
150                 printf("     Firmware: %s (%s)\n", info->fw_type, strna(info->fw_info));
151         if (info->fw_secure_boot >= 0)
152                 printf("  Secure Boot: %s\n", info->fw_secure_boot ? "enabled" : "disabled");
153         if (info->fw_secure_boot_setup_mode >= 0)
154                 printf("   Setup Mode: %s\n", info->fw_secure_boot_setup_mode ? "setup" : "user");
155         printf("\n");
156
157         if (info->fw_entry_active >= 0) {
158                 printf("Selected Firmware Entry:\n");
159                 printf("        Title: %s\n", strna(info->fw_entries[info->fw_entry_active].title));
160                 if (!sd_id128_equal(info->fw_entries[info->fw_entry_active].part_uuid, SD_ID128_NULL))
161                         printf("    Partition: /dev/disk/by-partuuid/%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n",
162                                SD_ID128_FORMAT_VAL(info->fw_entries[info->fw_entry_active].part_uuid));
163                 else
164                         printf("    Partition: n/a\n");
165                 if (info->fw_entries[info->fw_entry_active].path)
166                         printf("         File: %s%s\n", draw_special_char(DRAW_TREE_RIGHT), info->fw_entries[info->fw_entry_active].path);
167         }
168         printf("\n");
169
170         if (info->loader) {
171                 printf("Boot Loader:\n");
172                 printf("      Product: %s\n", info->loader);
173                 if (!sd_id128_equal(info->loader_part_uuid, SD_ID128_NULL))
174                         printf("    Partition: /dev/disk/by-partuuid/%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n",
175                                SD_ID128_FORMAT_VAL(info->loader_part_uuid));
176                         else
177                                 printf("    Partition: n/a\n");
178                 printf("         File: %s%s\n", draw_special_char(DRAW_TREE_RIGHT), strna(info->loader_image_path));
179                 printf("\n");
180
181                 if (info->loader_entry_active >= 0) {
182                         printf("Selected Boot Loader Entry:\n");
183                         printf("        Title: %s\n", strna(info->loader_entries[info->loader_entry_active].title));
184                         printf("         File: %s\n", info->loader_entries[info->loader_entry_active].path);
185                         if (info->loader_options_added)
186                                 printf("      Options: %s\n", info->loader_options_added);
187                 }
188         } else
189                 printf("No suitable data is provided by the boot manager. See:\n"
190                        "  http://www.freedesktop.org/wiki/Software/systemd/BootLoaderInterface\n"
191                        "  http://www.freedesktop.org/wiki/Specifications/BootLoaderSpec\n"
192                        "for details.\n");
193         printf("\n");
194
195         boot_info_free(info);
196         return err;
197 }
198
199 static int bootctl_main(int argc, char *argv[]) {
200         static const struct {
201                 const char* verb;
202                 const enum {
203                         MORE,
204                         LESS,
205                         EQUAL
206                 } argc_cmp;
207                 const int argc;
208                 int (* const dispatch)(char **args, unsigned n);
209         } verbs[] = {
210                 { "status",                LESS,   1, show_status      },
211         };
212
213         int left;
214         unsigned i;
215
216         assert(argc >= 0);
217         assert(argv);
218
219         left = argc - optind;
220
221         if (left <= 0)
222                 /* Special rule: no arguments means "status" */
223                 i = 0;
224         else {
225                 if (streq(argv[optind], "help")) {
226                         help();
227                         return 0;
228                 }
229
230                 for (i = 0; i < ELEMENTSOF(verbs); i++)
231                         if (streq(argv[optind], verbs[i].verb))
232                                 break;
233
234                 if (i >= ELEMENTSOF(verbs)) {
235                         log_error("Unknown operation %s", argv[optind]);
236                         return -EINVAL;
237                 }
238         }
239
240         switch (verbs[i].argc_cmp) {
241
242         case EQUAL:
243                 if (left != verbs[i].argc) {
244                         log_error("Invalid number of arguments.");
245                         return -EINVAL;
246                 }
247                 break;
248
249         case MORE:
250                 if (left < verbs[i].argc) {
251                         log_error("Too few arguments.");
252                         return -EINVAL;
253                 }
254                 break;
255
256         case LESS:
257                 if (left > verbs[i].argc) {
258                         log_error("Too many arguments.");
259                         return -EINVAL;
260                 }
261                 break;
262
263         default:
264                 assert_not_reached("Unknown comparison operator.");
265         }
266
267         return verbs[i].dispatch(argv + optind, left);
268 }
269
270 int main(int argc, char *argv[]) {
271         int r, retval = EXIT_FAILURE;
272
273         log_parse_environment();
274         log_open();
275
276         r = parse_argv(argc, argv);
277         if (r < 0)
278                 goto finish;
279         else if (r == 0) {
280                 retval = EXIT_SUCCESS;
281                 goto finish;
282         }
283
284         r = bootctl_main(argc, argv);
285         retval = r < 0 ? EXIT_FAILURE : r;
286 finish:
287         return retval;
288 }