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