1c9709a11f950f55b491778723bbbb3ba0158e3c
[debian/elilo] / tools / eliloalt.c
1 /*
2  * eliloalt.c
3  *
4  * Copyright (C) 2002-2003 Hewlett-Packard Co
5  * Contributed by Stephane Eranian <eranian@hpl.hp.com>
6  *
7  * This file is part of the ELILO, the EFI Linux boot loader.
8  *
9  *  ELILO is free software; you can redistribute it and/or modify
10  *  it under the terms of the GNU General Public License as published by
11  *  the Free Software Foundation; either version 2, or (at your option)
12  *  any later version.
13  *
14  *  ELILO is distributed in the hope that it will be useful,
15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  *  GNU General Public License for more details.
18  *
19  *  You should have received a copy of the GNU General Public License
20  *  along with ELILO; see the file COPYING.  If not, write to the Free
21  *  Software Foundation, 59 Temple Place - Suite 330, Boston, MA
22  *  02111-1307, USA.
23  *
24  */
25
26 /*
27  * This program is used to set the EliloAlt EFI variable to influence
28  * how elilo will behave at the next reboot. This variable is used
29  * to boot a certain kernel/configuration only once (debug, for instance).
30  *
31  * This is is supposed to be installed in /usr/sbin/eliloalt and must only
32  * be run by root.
33  */
34 #include <sys/types.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <stdarg.h>
38 #include <fcntl.h>
39 #include <unistd.h>
40 #include <getopt.h>
41 #include <errno.h>
42 #include <stdint.h>
43 #include <string.h>
44 #include <dirent.h>
45
46
47 #define ELILOALT_VERSION        "0.02"
48
49 #define ELILO_ALT_NAME  "EliloAlt"
50 #define EFIVAR_DIR      "/proc/efi/vars"
51 #define ELILO_ALTVAR    EFIVAR_DIR"/"ELILO_ALT_NAME"-00000000-0000-0000-0000-000000000000"
52
53 #define EFI_VARIABLE_NON_VOLATILE 0x0000000000000001
54 #define EFI_VARIABLE_BOOTSERVICE_ACCESS 0x0000000000000002
55 #define EFI_VARIABLE_RUNTIME_ACCESS 0x0000000000000004
56
57 typedef unsigned long efi_status_t;
58 typedef uint8_t  efi_bool_t;
59 typedef uint16_t efi_char16_t;          /* UNICODE character */
60
61 /*
62  * EFI GUID type definition
63  */
64 typedef struct {
65         uint32_t data1;
66         uint16_t data2;
67         uint16_t data3;
68         uint8_t  data4[8];
69 } efi_guid_t;
70
71 /*
72  * EFI variable structure 
73  */
74 typedef struct _efi_variable_t {
75         efi_char16_t    variablename[1024/sizeof(efi_char16_t)];
76         efi_guid_t      vendorguid;
77         uint64_t        datasize;
78         uint8_t         data[1024];
79         efi_status_t    status;
80         uint32_t        attributes;
81 } __attribute__((packed)) efi_variable_t;
82
83 static char *elilo_alt_name = ELILO_ALT_NAME;
84
85 static struct option cmd_options[]={
86         { "version", 0, 0, 1},
87         { "help", 0, 0, 2},
88         { "delete", 0, 0, 3},
89         { "print", 0, 0, 4},
90         { "set", 1, 0, 5},
91         { 0, 0, 0, 0}
92 };
93
94 static void fatal_error(char *fmt,...) __attribute__((noreturn));
95
96 static void
97 fatal_error(char *fmt, ...) 
98 {
99         va_list ap;
100
101         va_start(ap, fmt);
102         vfprintf(stderr, fmt, ap);
103         va_end(ap);
104
105         exit(1);
106 }
107
108
109 static void
110 usage(char **argv)
111 {
112         printf("Usage: %s [OPTIONS] cmdline\n", argv[0]);
113
114         printf( "-h, --help\t\tdisplay this help and exit\n"
115                 "--version\t\toutput version information and exit\n"
116                 "-s, --set cmdline\tset elilo alternate variable to cmdline\n"
117                 "-p, --print\t\tprint elilo alternate variable\n"
118                 "-d, --delete\t\tprint elilo alternate variable\n"
119         );
120 }
121
122 static char *
123 check_proc_efi(int find_entry)
124 {
125         DIR *efi_vars;
126         struct dirent *entry;
127         static char name[1024];
128
129         if (getuid() != 0) {
130                 fatal_error("This program must be run as root\n");
131         }
132         efi_vars = opendir(EFIVAR_DIR);
133         if (efi_vars == NULL) {
134                 fatal_error("Cannot access %s\n", EFIVAR_DIR);
135         }
136         if (!find_entry) {
137                 closedir(efi_vars);
138                 return NULL;
139         }
140         /* Find one entry we can open */
141         while ((entry = readdir(efi_vars)) != NULL) {
142                 if (strcmp(entry->d_name, ".") && strcmp(entry->d_name, ".."))
143                         break;
144         }
145         if (entry == NULL) {
146                 fatal_error("Cannot find entry in %s\n", EFIVAR_DIR);
147         }
148         sprintf(name, "%s/%s", EFIVAR_DIR, entry->d_name);
149         closedir(efi_vars);
150         return name;
151 }
152
153 static void
154 delete_var(void)
155 {
156         efi_variable_t var;
157         int fd, r, i;
158
159         check_proc_efi(0);
160
161         fd = open(ELILO_ALTVAR, O_WRONLY);
162         if (fd == -1) {
163                 fatal_error("variable not defined\n");
164         }
165
166         memset(&var, 0, sizeof(var));
167
168         for (i=0; i < sizeof(elilo_alt_name); i++) {
169                 var.variablename[i] = (efi_char16_t)elilo_alt_name[i];
170         }
171
172         /* 
173          * we use NULL GUID so no need to initialize it now memset() did it
174          * writing with a datasize=0 will effectively delete the variable.
175          */
176         
177         r = write(fd, &var, sizeof(var));
178         if (r != sizeof(var)) {
179                 fatal_error("Variable %s defined but invalid content\n", ELILO_ALTVAR);
180         }
181         close(fd);
182 }
183
184
185 static void
186 print_var(void)
187 {
188         efi_variable_t var;
189         int fd, r, i;
190
191
192         check_proc_efi(0);
193
194         fd = open(ELILO_ALTVAR, O_RDONLY);
195         if (fd == -1) {
196                 fatal_error("variable not defined\n");
197         }
198
199         memset(&var, 0, sizeof(var));
200
201         r = read(fd, &var, sizeof(var));
202         if (r != sizeof(var)) {
203                 fatal_error("Variable %s defined but invalid content\n", ELILO_ALTVAR);
204         }
205         printf("EliloAlt=\"");
206         for(i=0; i < var.datasize; i+=1){
207                 printf("%c", var.data[i]);
208         }
209         printf("\"\n");
210         close(fd);
211 }
212
213 static void
214 set_var(char *cmdline)
215 {
216         efi_variable_t var;
217         int fd, r, i, j, l;
218         char *name;
219
220         name = check_proc_efi(1);
221
222         if (cmdline == NULL) {
223                 fatal_error("invalid cmdline argument\n");
224         }
225
226         l = strlen(cmdline);
227
228         if (l >= 1024) {
229                 fatal_error("Variable content is too long, must be <= 512 characters\n");
230         }
231
232         fd = open(name, O_WRONLY);
233         if (fd == -1) {
234                 fatal_error("can't open %s: %s\n", ELILO_ALTVAR, strerror(errno));
235         }
236
237         memset(&var, 0, sizeof(var));
238
239         for (i=0; i < sizeof(elilo_alt_name); i++) {
240                 var.variablename[i] = (efi_char16_t)elilo_alt_name[i];
241         }
242
243         for (i=0, j=0; i < l; i++, j+=2) {
244                 var.data[j] = (efi_char16_t)cmdline[i];
245         }
246         /* +2 = include char16 for null termination */
247         var.datasize   = j+2;
248
249         var.attributes =  EFI_VARIABLE_NON_VOLATILE 
250                         | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS;
251
252         /* 
253          * we use NULL GUID so no need to initialize it now memset() did it
254          * writing with a datasize=0 will effectively delete the variable.
255          */
256         
257         r = write(fd, &var, sizeof(var));
258         if (r != sizeof(var)) {
259                 fatal_error("Variable %s defined but invalid content %d\n", ELILO_ALTVAR, r);
260         }
261         close(fd);
262
263 }
264
265 int
266 main(int argc, char **argv)
267 {
268         int c;
269
270         while ((c=getopt_long(argc, argv,"hdps:", cmd_options, 0)) != -1) {
271                 switch(c) {
272                         case   0: continue; /* fast path for options */
273                         case   1:
274                                 printf("Version %s Date: %s\n", ELILOALT_VERSION, __DATE__);
275                                 exit(0);
276                         case   2:
277                         case 'h':
278                                 usage(argv);
279                                 exit(0);
280                         case   3:
281                         case 'd':
282                                 delete_var();
283                                 exit(0);
284                         case   4:
285                         case 'p':
286                                 print_var();
287                                 exit(0);
288                         case   5:
289                         case 's':
290                                 set_var(optarg);
291                                 exit(0);
292                         default:
293                                 fatal_error("Unknown option\n");
294                 }
295         }
296         print_var();
297         return 0;
298 }