chiark / gitweb /
641546bc082f87d3d046649e029ba90e74d4a16f
[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 mananger settings.\n\n"
38                "  -h --help              Show this help\n"
39                "     --version           Show package version\n"
40                "Commands:\n"
41                "  status                 Show current time 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("    Machine ID: %s\n", sd_id128_to_string(info->machine_id, buf));
147         printf("       Boot ID: %s\n", sd_id128_to_string(info->boot_id, buf));
148         if (info->fw_type)
149                 printf("      Firmware: %s (%s)\n", info->fw_type, strna(info->fw_info));
150
151         if (info->fw_entry_active >= 0) {
152                 printf("Firmware entry: %s\n", info->fw_entries[info->fw_entry_active].title);
153                 if (info->fw_entries[info->fw_entry_active].path)
154                         printf("                %s\n", info->fw_entries[info->fw_entry_active].path);
155                 if (!sd_id128_equal(info->fw_entries[info->fw_entry_active].part_uuid, SD_ID128_NULL))
156                         printf("                %s\n", sd_id128_to_string(info->fw_entries[info->fw_entry_active].part_uuid, buf));
157         }
158
159         if (info->loader) {
160                 printf("        Loader: %s\n", info->loader);
161                 printf("                %s\n", strna(info->loader_image_path));
162                 if (!sd_id128_equal(info->loader_part_uuid, SD_ID128_NULL))
163                         printf("                %s\n", sd_id128_to_string(info->loader_part_uuid, buf));
164
165                 if (info->loader_entry_active >= 0) {
166                         printf("  Loader entry: %s\n", info->loader_entries[info->loader_entry_active].title);
167                         printf("                %s\n", info->loader_entries[info->loader_entry_active].path);
168                 }
169
170                 printf("Loader options: %s\n", strna(info->loader_options_added));
171         } else
172                 printf("No suitable data is provided by the boot manager. See:\n"
173                        "  http://www.freedesktop.org/wiki/Software/systemd/BootLoaderInterface\n"
174                        "  http://www.freedesktop.org/wiki/Specifications/BootLoaderSpec\n"
175                        "for details.\n");
176         printf("\n");
177
178         boot_info_free(info);
179         return err;
180 }
181
182 static int bootctl_main(int argc, char *argv[]) {
183         static const struct {
184                 const char* verb;
185                 const enum {
186                         MORE,
187                         LESS,
188                         EQUAL
189                 } argc_cmp;
190                 const int argc;
191                 int (* const dispatch)(char **args, unsigned n);
192         } verbs[] = {
193                 { "status",                LESS,   1, show_status      },
194         };
195
196         int left;
197         unsigned i;
198
199         assert(argc >= 0);
200         assert(argv);
201
202         left = argc - optind;
203
204         if (left <= 0)
205                 /* Special rule: no arguments means "status" */
206                 i = 0;
207         else {
208                 if (streq(argv[optind], "help")) {
209                         help();
210                         return 0;
211                 }
212
213                 for (i = 0; i < ELEMENTSOF(verbs); i++)
214                         if (streq(argv[optind], verbs[i].verb))
215                                 break;
216
217                 if (i >= ELEMENTSOF(verbs)) {
218                         log_error("Unknown operation %s", argv[optind]);
219                         return -EINVAL;
220                 }
221         }
222
223         switch (verbs[i].argc_cmp) {
224
225         case EQUAL:
226                 if (left != verbs[i].argc) {
227                         log_error("Invalid number of arguments.");
228                         return -EINVAL;
229                 }
230                 break;
231
232         case MORE:
233                 if (left < verbs[i].argc) {
234                         log_error("Too few arguments.");
235                         return -EINVAL;
236                 }
237                 break;
238
239         case LESS:
240                 if (left > verbs[i].argc) {
241                         log_error("Too many arguments.");
242                         return -EINVAL;
243                 }
244                 break;
245
246         default:
247                 assert_not_reached("Unknown comparison operator.");
248         }
249
250         return verbs[i].dispatch(argv + optind, left);
251 }
252
253 int main(int argc, char *argv[]) {
254         int r, retval = EXIT_FAILURE;
255
256         log_parse_environment();
257         log_open();
258
259         r = parse_argv(argc, argv);
260         if (r < 0)
261                 goto finish;
262         else if (r == 0) {
263                 retval = EXIT_SUCCESS;
264                 goto finish;
265         }
266
267         r = bootctl_main(argc, argv);
268         retval = r < 0 ? EXIT_FAILURE : r;
269 finish:
270         return retval;
271 }