Imported Debian patch 0.5.3-1
[debian/efibootmgr] / src / efibootmgr / efibootmgr.c
1 /*
2   efibootmgr.c - Manipulates EFI variables as exported in /proc/efi/vars
3
4   Copyright (C) 2001-2004 Dell, Inc. <Matt_Domsch@dell.com>
5
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with this program; if not, write to the Free Software
18     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19
20
21   This must tie the EFI_DEVICE_PATH to /boot/efi/elilo.efi
22   The  EFI_DEVICE_PATH will look something like:
23     ACPI device path, length 12 bytes
24     Hardware Device Path, PCI, length 6 bytes
25     Messaging Device Path, SCSI, length 8 bytes, or ATAPI, length ??
26     Media Device Path, Hard Drive, partition XX, length 30 bytes
27     Media Device Path, File Path, length ??
28     End of Hardware Device Path, length 4
29     Arguments passed to elilo, as UCS-2 characters, length ??
30
31 */
32
33 #define _GNU_SOURCE
34
35 #include <ctype.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <errno.h>
40 #include <stdint.h>
41 #include <sys/stat.h>
42 #include <fcntl.h>
43 #include <dirent.h>
44 #include <unistd.h>
45 #include <getopt.h>
46 #include "list.h"
47 #include "efi.h"
48 #include "efichar.h"
49 #include "unparse_path.h"
50 #include "disk.h"
51 #include "efibootmgr.h"
52
53
54 #ifndef EFIBOOTMGR_VERSION
55 #define EFIBOOTMGR_VERSION "unknown (fix Makefile!)"
56 #endif
57
58
59 typedef struct _var_entry {
60         struct dirent *name;
61         uint16_t       num;
62         efi_variable_t var_data;
63         list_t         list;
64 } var_entry_t;
65
66
67 /* global variables */
68 static  LIST_HEAD(boot_entry_list);
69 static  LIST_HEAD(blk_list);
70 efibootmgr_opt_t opts;
71
72 static inline void
73 var_num_from_name(const char *pattern, char *name, uint16_t *num)
74 {
75         sscanf(name, pattern, num);
76 }
77
78 static void
79 fill_bootvar_name(char *dest, size_t len, const char *name)
80 {
81         efi_guid_t guid = EFI_GLOBAL_VARIABLE;
82         char text_uuid[40];
83         efi_guid_unparse(&guid, text_uuid);
84         snprintf(dest, len, "%s-%s", name, text_uuid);
85 }
86
87 static void
88 fill_var(efi_variable_t *var, const char *name)
89 {
90         efi_guid_t guid = EFI_GLOBAL_VARIABLE;
91
92         efichar_from_char(var->VariableName, name, 1024);
93         memcpy(&var->VendorGuid, &guid, sizeof(guid));
94         var->Attributes = EFI_VARIABLE_NON_VOLATILE
95                 | EFI_VARIABLE_BOOTSERVICE_ACCESS
96                 | EFI_VARIABLE_RUNTIME_ACCESS;
97 }
98
99 static void
100 free_vars(list_t *head)
101 {
102         list_t *pos, *n;
103         var_entry_t *boot;
104
105         list_for_each_safe(pos, n, head) {
106                 boot = list_entry(pos, var_entry_t, list);
107                 list_del(&(boot->list));
108                 free(boot);
109         }
110 }
111
112 static void
113 read_vars(struct dirent **namelist,
114           int num_boot_names,
115           list_t *head)
116 {
117         efi_status_t status;
118         var_entry_t *entry;
119         int i;
120
121         if (!namelist) return;
122
123         for (i=0; i < num_boot_names; i++)
124         {
125                 if (namelist[i]) {
126                         entry = malloc(sizeof(var_entry_t));
127                         if (!entry) return;
128                         memset(entry, 0, sizeof(var_entry_t));
129
130                         status = read_variable(namelist[i]->d_name,
131                                                &entry->var_data);
132                         if (status != EFI_SUCCESS) break;
133                         entry->name = namelist[i];
134                         list_add_tail(&entry->list, head);
135                 }
136         }
137         return;
138 }
139
140
141
142
143
144 static void
145 free_dirents(struct dirent **ptr, int num_dirents)
146 {
147         int i;
148         if (!ptr) return;
149         for (i=0; i < num_dirents; i++) {
150                 if (ptr[i]) {
151                         free(ptr[i]);
152                         ptr[i] = NULL;
153                 }
154         }
155         free(ptr);
156 }
157
158
159
160 static int
161 compare(const void *a, const void *b)
162 {
163         int rc = -1;
164         uint32_t n1, n2;
165         memcpy(&n1, a, sizeof(n1));
166         memcpy(&n2, b, sizeof(n2));
167         if (n1 < n2) rc = -1;
168         if (n1 == n2) rc = 0;
169         if (n2 > n2) rc = 1;
170         return rc;
171 }
172
173
174 /*
175   Return an available boot variable number,
176   or -1 on failure.
177 */
178 static int
179 find_free_boot_var(list_t *boot_list)
180 {
181         int num_vars=0, i=0, found;
182         uint16_t *vars, free_number;
183         list_t *pos;
184         var_entry_t *boot;
185         list_for_each(pos, boot_list) {
186                 num_vars++;
187         }
188         vars = malloc(sizeof(uint16_t) * num_vars);
189         if (!vars) return -1;
190         memset(vars, 0, sizeof(uint16_t) * num_vars);
191
192         list_for_each(pos, boot_list) {
193                 boot = list_entry(pos, var_entry_t, list);
194                 vars[i] = boot->num;
195                         i++;
196         }
197         qsort(vars, i, sizeof(uint16_t), compare);
198         found = 1;
199
200         num_vars = i;
201         for (free_number = 0; free_number < num_vars && found; free_number++) {
202                 found = 0;
203                 list_for_each(pos, boot_list) {
204                         boot = list_entry(pos, var_entry_t, list);
205                         if (boot->num == free_number) {
206                                 found = 1;
207                                 break;
208                         }
209                 }
210                 if (!found) break;
211         }
212         if (found && num_vars) free_number = vars[num_vars-1] + 1;
213         free(vars);
214         return free_number;
215 }
216
217
218 static void
219 warn_duplicate_name(list_t *boot_list)
220 {
221         list_t *pos;
222         var_entry_t *boot;
223         EFI_LOAD_OPTION *load_option;
224
225         list_for_each(pos, boot_list) {
226                 boot = list_entry(pos, var_entry_t, list);
227                 load_option = (EFI_LOAD_OPTION *)
228                         boot->var_data.Data;
229                 if (!efichar_char_strcmp(opts.label,
230                                          load_option->description)) {
231                         fprintf(stderr, "** Warning ** : %.8s has same label %s\n",
232                                boot->name->d_name,
233                                opts.label);
234                 }
235         }
236 }
237
238
239 static var_entry_t *
240 make_boot_var(list_t *boot_list)
241 {
242         var_entry_t *boot;
243         int free_number;
244         list_t *pos;
245
246         if (opts.bootnum == -1)
247                 free_number = find_free_boot_var(boot_list);
248         else {
249                 list_for_each(pos, boot_list) {
250                         boot = list_entry(pos, var_entry_t, list);
251                         if (boot->num == opts.bootnum) {
252                                 fprintf(stderr, "** Warning ** : bootnum %04X "
253                                         "already exists\n", opts.bootnum);
254                                 return NULL;
255                         }
256                 }
257                 free_number = opts.bootnum;
258         }
259
260         if (free_number == -1) return NULL;
261
262         /* Create a new var_entry_t object
263            and populate it.
264         */
265
266         boot = malloc(sizeof(*boot));
267         if (!boot) return NULL;
268         memset(boot, 0, sizeof(*boot));
269         boot->num = free_number;
270         if (!make_linux_efi_variable(&boot->var_data, free_number)) {
271                 free(boot);
272                 return NULL;
273         }
274         create_variable(&boot->var_data);
275         list_add_tail(&boot->list, boot_list);
276         return boot;
277 }
278
279
280
281 static efi_status_t
282 read_boot(efi_variable_t *var, const char *name)
283 {
284         char name_guid[PATH_MAX];
285
286         memset(var, 0, sizeof(*var));
287         fill_bootvar_name(name_guid, sizeof(name_guid), name);
288         return read_variable(name_guid, var);
289 }
290
291 static efi_status_t
292 read_boot_order(efi_variable_t *boot_order)
293 {
294         efi_status_t status;
295
296         status = read_boot(boot_order, "BootOrder");
297         if (status != EFI_SUCCESS && status != EFI_NOT_FOUND)
298                 return status;
299
300         if (status == EFI_NOT_FOUND) {
301                 fill_var(boot_order, "BootOrder");
302         }
303         return EFI_SUCCESS;
304 }
305
306
307 static efi_status_t
308 add_to_boot_order(uint16_t num)
309 {
310         efi_status_t status;
311         efi_variable_t boot_order;
312         uint64_t new_data_size;
313         uint16_t *new_data, *old_data;
314
315         status = read_boot_order(&boot_order);
316         if (status != EFI_SUCCESS) return status;
317
318         /* We've now got an array (in boot_order.Data) of the
319            boot order.  First add our entry, then copy the old array.
320         */
321         old_data = (uint16_t *)&(boot_order.Data);
322         new_data_size = boot_order.DataSize + sizeof(uint16_t);
323         new_data = malloc(new_data_size);
324
325         new_data[0] = num;
326         memcpy(new_data+1, old_data, boot_order.DataSize);
327
328         /* Now new_data has what we need */
329         memcpy(&(boot_order.Data), new_data, new_data_size);
330         boot_order.DataSize = new_data_size;
331         return create_or_edit_variable(&boot_order);
332 }
333
334
335 static efi_status_t
336 remove_from_boot_order(uint16_t num)
337 {
338         efi_status_t status;
339         efi_variable_t boot_order;
340         uint64_t new_data_size;
341         uint16_t *new_data, *old_data;
342         int old_i,new_i;
343         char boot_order_name[PATH_MAX];
344
345         status = read_boot_order(&boot_order);
346         if (status != EFI_SUCCESS) return status;
347         /* If it's empty, yea! */
348         if (!boot_order.DataSize) return EFI_SUCCESS;
349
350         fill_bootvar_name(boot_order_name, sizeof(boot_order_name),
351                           "BootOrder");
352
353         /* We've now got an array (in boot_order.Data) of the
354            boot order.  Simply copy the array, skipping the
355            entry we're deleting.
356         */
357         old_data = (uint16_t *)&(boot_order.Data);
358         /* Start with the same size */
359         new_data_size = boot_order.DataSize;
360         new_data = malloc(new_data_size);
361         for (old_i=0,new_i=0;
362              old_i < boot_order.DataSize / sizeof(uint16_t);
363              old_i++) {
364                 if (old_data[old_i] != num) {
365                                 /* Copy this value */
366                         new_data[new_i] = old_data[old_i];
367                         new_i++;
368                 }
369         }
370
371         /* Now new_data has what we need */
372         new_data_size = new_i * sizeof(uint16_t);
373         memset(&(boot_order.Data), 0, boot_order.DataSize);
374         memcpy(&(boot_order.Data), new_data, new_data_size);
375         boot_order.DataSize = new_data_size;
376
377         return edit_variable(&boot_order);
378 }
379
380 static efi_status_t
381 delete_var(const char *name)
382 {
383         efi_variable_t var;
384
385         memset(&var, 0, sizeof(var));
386         fill_var(&var, name);
387         return delete_variable(&var);
388 }
389
390 static int
391 read_boot_u16(const char *name)
392 {
393         efi_status_t status;
394         efi_variable_t var;
395         uint16_t *n = (uint16_t *)(var.Data);
396
397         memset(&var, 0, sizeof(var));
398         status = read_boot(&var, name);
399         if (status) return -1;
400         return *n;
401 }
402
403 static efi_status_t
404 set_boot_u16(const char *name, uint16_t num)
405 {
406         efi_variable_t var;
407         uint16_t *n = (uint16_t *)var.Data;
408
409         memset(&var, 0, sizeof(var));
410
411         fill_var(&var, name);
412         *n = num;
413         var.DataSize = sizeof(uint16_t);
414         return create_or_edit_variable(&var);
415 }
416
417 static efi_status_t
418 delete_boot_var(uint16_t num)
419 {
420         efi_status_t status;
421         efi_variable_t var;
422         char name[16];
423         list_t *pos, *n;
424         var_entry_t *boot;
425
426         snprintf(name, sizeof(name), "Boot%04X", num);
427         memset(&var, 0, sizeof(var));
428         fill_var(&var, name);
429         status = delete_variable(&var);
430
431         /* For backwards compatibility, try to delete abcdef entries as well */
432         if (status) {
433                 snprintf(name, sizeof(name), "Boot%04x", num);
434                 memset(&var, 0, sizeof(var));
435                 fill_var(&var, name);
436                 status = delete_variable(&var);
437         }
438
439         if (status) return status;
440         list_for_each_safe(pos, n, &boot_entry_list) {
441                 boot = list_entry(pos, var_entry_t, list);
442                 if (boot->num == num) {
443                         status = remove_from_boot_order(num);
444                         if (status) return status;
445                         list_del(&(boot->list));
446                         break; /* short-circuit since it was found */
447                 }
448         }
449         return EFI_SUCCESS;
450 }
451
452
453 static void
454 set_var_nums(const char *pattern, list_t *list)
455 {
456         list_t *pos;
457         var_entry_t *var;
458         int num=0, rc;
459         char *name;
460         int warn=0;
461
462         list_for_each(pos, list) {
463                 var = list_entry(pos, var_entry_t, list);
464                 rc = sscanf(var->name->d_name, pattern, &num);
465                 if (rc == 1) {
466                         var->num = num;
467                         name = var->name->d_name; /* shorter name */
468                         if ((isalpha(name[4]) && islower(name[4])) ||
469                             (isalpha(name[5]) && islower(name[5])) ||
470                             (isalpha(name[6]) && islower(name[6])) ||
471                             (isalpha(name[7]) && islower(name[7]))) {
472                                 fprintf(stderr, "** Warning ** : %.8s is not "
473                                         "EFI 1.10 compliant (lowercase hex in name)\n", name);
474                                 warn++;
475                         }
476                 }
477         }
478         if (warn) {
479                 fprintf(stderr, "** Warning ** : please recreate these using efibootmgr to remove this warning.\n");
480         }
481 }
482
483 #if 0
484 static efi_variable_t *
485 find_pci_scsi_disk_blk(int fd, int bus, int device, int func,
486                        list_t *blk_list)
487 {
488         list_t *pos;
489         int rc;
490         Scsi_Idlun idlun;
491         unsigned char host, channel, id, lun;
492         var_entry_t *blk;
493         efi_variable_t *blk_var;
494         long size = 0;
495
496         memset(&idlun, 0, sizeof(idlun));
497         rc = get_scsi_idlun(fd, &idlun);
498         if (rc) return NULL;
499
500         rc = disk_get_size(fd, &size);
501
502         idlun_to_components(&idlun, &host, &channel, &id, &lun);
503
504         list_for_each(pos, blk_list) {
505                 blk = list_entry(pos, var_entry_t, list);
506                 blk_var = blk->var_data;
507
508                 if (!compare_pci_scsi_disk_blk(blk_var,
509                                                bus, device, func,
510                                                host, channel, id, lun,
511                                                0, size)) {
512                         return blk_var;
513                 }
514         }
515         return NULL;
516 }
517
518
519
520
521 /* The right blkX variable contains:
522    1) the PCI and SCSI information for the disk passed in disk_name
523    2) Does not contain a partition field 
524 */
525
526
527 static efi_variable_t *
528 find_disk_blk(char *disk_name, list_t *blk_list)
529 {
530         efi_variable_t *disk_blk = NULL;
531         int fd, rc;
532         unsigned char bus=0,device=0,func=0;
533         int interface_type=interface_type_unknown;
534         unsigned int controllernum=0, disknum=0;
535         unsigned char part=0;
536
537         fd = open(disk_name, O_RDONLY|O_DIRECT);
538         rc = disk_get_pci(fd, &bus, &device, &func);
539         if (rc) {
540                 fprintf(stderr, "disk_get_pci() failed.\n");
541                 return NULL;
542         }
543         rc = disk_info_from_fd(fd,
544                                &interface_type,
545                                &controllernum,
546                                &disknum,
547                                &part);
548         if (rc) {
549                 fprintf(stderr, "disk_info_from_fd() failed.\n");
550                 return NULL;
551         }
552         switch (interface_type)
553         {
554         case scsi:
555                 return find_pci_scsi_disk_blk(fd,bus,device,func,blk_list);
556                 break;
557         case ata:
558                 return find_pci_ata_disk_blk(fd,bus,device,func,blk_list);
559                 break;
560         case i2o:
561                 return find_pci_i2o_disk_blk(fd,bus,device,func,blk_list);
562                 break;
563         case md:
564                 return find_pci_md_disk_blk(fd,bus,device,func,blk_list);
565                 break;
566         default:
567                 break;
568         }
569         return NULL;
570 }
571 #endif
572
573 static void
574 unparse_boot_order(uint16_t *order, int length)
575 {
576         int i;
577         printf("BootOrder: ");
578         for (i=0; i<length; i++) {
579                 printf("%04X", order[i]);
580                 if (i < (length-1))
581                         printf(",");
582         }
583         printf("\n");
584 }
585
586 static int
587 parse_boot_order(char *buffer, uint16_t *order, int length)
588 {
589         int i;
590         int num, rc;
591
592         for (i=0; i<length && *buffer; i++) {
593                 rc = sscanf(buffer, "%x", &num);
594                 if (rc == 1) order[i] = num & 0xFFFF;
595                 /* Advance to the comma */ 
596                 while (*buffer && *buffer != ',') buffer++;
597                 /* Advance through the comma(s) */
598                 while (*buffer && *buffer == ',') buffer++;
599         }
600         return i;
601 }
602
603 static efi_status_t
604 set_boot_order()
605 {
606         efi_variable_t boot_order;
607         uint16_t *n = (uint16_t *)boot_order.Data;
608
609         if (!opts.bootorder) return EFI_SUCCESS;
610
611         memset(&boot_order, 0, sizeof(boot_order));
612         fill_var(&boot_order, "BootOrder");
613
614         boot_order.DataSize = parse_boot_order(opts.bootorder, n, 1024/sizeof(uint16_t)) * sizeof(uint16_t);
615         return create_or_edit_variable(&boot_order);
616 }
617
618 static void
619 show_boot_vars()
620 {
621         list_t *pos;
622         var_entry_t *boot;
623         char description[80];
624         EFI_LOAD_OPTION *load_option;
625         EFI_DEVICE_PATH *path;
626         char text_path[1024], *p;
627         unsigned long optional_data_len=0;
628
629         list_for_each(pos, &boot_entry_list) {
630                 boot = list_entry(pos, var_entry_t, list);
631                 load_option = (EFI_LOAD_OPTION *)
632                         boot->var_data.Data;
633                 efichar_to_char(description,
634                                 load_option->description, sizeof(description));
635                 memset(text_path, 0, sizeof(text_path));
636                 path = load_option_path(load_option);
637                 if (boot->name)
638                         printf("%.8s", boot->name->d_name);
639                 else
640                         printf("Boot%04X", boot->num);
641
642                 if (load_option->attributes & LOAD_OPTION_ACTIVE)
643                         printf("* ");
644                 else    printf("  ");
645                 printf("%s", description);
646
647                 if (opts.verbose) {
648                         unparse_path(text_path, path,
649                                      load_option->file_path_list_length);
650                         /* Print optional data */
651                         optional_data_len =
652                                 boot->var_data.DataSize -
653                                 load_option->file_path_list_length -
654                                 ((char *)path - (char *)load_option);
655                         if (optional_data_len) {
656                                 p = text_path;
657                                 p += strlen(text_path);
658                                 unparse_raw_text(p, ((uint8_t *)path) +
659                                                  load_option->file_path_list_length,
660                                                  optional_data_len);
661                         }
662
663                         printf("\t%s", text_path);
664                 }
665                 printf("\n");
666         }
667 }
668
669
670
671 static void
672 show_boot_order()
673 {
674         efi_status_t status;
675         efi_variable_t boot_order;
676         uint16_t *data;
677
678         status = read_boot_order(&boot_order);
679
680         if (status != EFI_SUCCESS) {
681                 perror("show_boot_order()");
682                 return;
683         }
684
685         /* We've now got an array (in boot_order.Data) of the
686            boot order.  First add our entry, then copy the old array.
687         */
688         data = (uint16_t *)&(boot_order.Data);
689         if (boot_order.DataSize)
690                 unparse_boot_order(data, boot_order.DataSize / sizeof(uint16_t));
691
692 }
693
694 static efi_status_t
695 set_active_state()
696 {
697         list_t *pos;
698         var_entry_t *boot;
699         EFI_LOAD_OPTION *load_option;
700
701         list_for_each(pos, &boot_entry_list) {
702                 boot = list_entry(pos, var_entry_t, list);
703                 load_option = (EFI_LOAD_OPTION *)
704                         boot->var_data.Data;
705                 if (boot->num == opts.bootnum) {
706                         if (opts.active == 1) {
707                                 if (load_option->attributes
708                                     & LOAD_OPTION_ACTIVE) return EFI_SUCCESS;
709                                 else {
710                                         load_option->attributes
711                                                 |= LOAD_OPTION_ACTIVE;
712                                         return edit_variable(&boot->var_data);
713                                 }
714                         }
715                         else if (opts.active == 0) {
716                                 if (!(load_option->attributes
717                                       & LOAD_OPTION_ACTIVE))
718                                         return EFI_SUCCESS;
719                                 else {
720                                         load_option->attributes
721                                                 &= ~LOAD_OPTION_ACTIVE;
722                                         return edit_variable(&boot->var_data);
723                                 }
724                         }
725                 }
726         }
727         return EFI_SUCCESS;
728 }
729
730
731
732
733 static void
734 usage()
735 {
736         printf("efibootmgr version %s\n", EFIBOOTMGR_VERSION);
737         printf("usage: efibootmgr [options]\n");
738         printf("\t-a | --active         sets bootnum active\n");
739         printf("\t-A | --inactive       sets bootnum inactive\n");
740         printf("\t-b | --bootnum XXXX   modify BootXXXX (hex)\n");
741         printf("\t-B | --delete-bootnum delete bootnum (hex)\n");
742         printf("\t-c | --create         create new variable bootnum and add to bootorder\n");
743         printf("\t-d | --disk disk       (defaults to /dev/sda) containing loader\n");
744         printf("\t-e | --edd [1|3|-1]   force EDD 1.0 or 3.0 creation variables, or guess\n");
745         printf("\t-E | --device num      EDD 1.0 device number (defaults to 0x80)\n");
746         printf("\t-g | --gpt            force disk with invalid PMBR to be treated as GPT\n");
747         printf("\t-H | --acpi_hid XXXX  set the ACPI HID (used with -i)\n");
748         printf("\t-i | --iface name     create a netboot entry for the named interface\n");
749         printf("\t-l | --loader name     (defaults to \\elilo.efi)\n");
750         printf("\t-L | --label label     Boot manager display label (defaults to \"Linux\")\n");
751         printf("\t-n | --bootnext XXXX   set BootNext to XXXX (hex)\n");
752         printf("\t-N | --delete-bootnext delete BootNext\n");
753         printf("\t-o | --bootorder XXXX,YYYY,ZZZZ,...     explicitly set BootOrder (hex)\n");
754         printf("\t-O | --delete-bootorder delete BootOrder\n");
755         printf("\t-p | --part part        (defaults to 1) containing loader\n");
756         printf("\t-q | --quiet            be quiet\n");
757         printf("\t   | --test filename    don't write to NVRAM, write to filename.\n");
758         printf("\t-t | --timeout seconds  set boot manager timeout waiting for user input.\n");
759         printf("\t-T | --delete-timeout   delete Timeout.\n");
760         printf("\t-u | --unicode | --UCS-2  pass extra args as UCS-2 (default is ASCII)\n");
761         printf("\t-U | --acpi_uid XXXX    set the ACPI UID (used with -i)\n");
762         printf("\t-v | --verbose          print additional information\n");
763         printf("\t-V | --version          return version and exit\n");
764         printf("\t-w | --write-signature  write unique sig to MBR if needed\n");
765         printf("\t-@ | --append-binary-args file  append extra args from file (use \"-\" for stdin)\n");
766 }
767
768 static void
769 set_default_opts()
770 {
771         memset(&opts, 0, sizeof(opts));
772         opts.bootnum         = -1;   /* auto-detect */
773         opts.bootnext        = -1;   /* Don't set it */
774         opts.active          = -1;   /* Don't set it */
775         opts.timeout         = -1;   /* Don't set it */
776         opts.edd10_devicenum = 0x80;
777         opts.loader          = "\\elilo.efi";
778         opts.label           = "Linux";
779         opts.disk            = "/dev/sda";
780         opts.iface           = NULL;
781         opts.part            = 1;
782         opts.acpi_hid        = -1;
783         opts.acpi_uid        = -1;
784 }
785
786 static void
787 parse_opts(int argc, char **argv)
788 {
789         int c, num, rc;
790         int option_index = 0;
791
792         while (1)
793         {
794                 static struct option long_options[] =
795                         /* name, has_arg, flag, val */
796                 {
797                         {"active",                 no_argument, 0, 'a'},
798                         {"inactive",               no_argument, 0, 'A'},
799                         {"bootnum",          required_argument, 0, 'b'},
800                         {"delete-bootnum",         no_argument, 0, 'B'},
801                         {"create",                 no_argument, 0, 'c'},
802                         {"disk",             required_argument, 0, 'd'},
803                         {"iface",            required_argument, 0, 'i'},
804                         {"acpi_hid",         required_argument, 0, 'H'},
805                         {"edd-device",       required_argument, 0, 'E'},
806                         {"edd30",            required_argument, 0, 'e'},
807                         {"gpt",                    no_argument, 0, 'g'},
808                         {"loader",           required_argument, 0, 'l'},
809                         {"label",            required_argument, 0, 'L'},
810                         {"bootnext",         required_argument, 0, 'n'},
811                         {"delete-bootnext",        no_argument, 0, 'N'},
812                         {"bootorder",        required_argument, 0, 'o'},
813                         {"delete-bootorder",       no_argument, 0, 'O'},
814                         {"part",             required_argument, 0, 'p'},
815                         {"quiet",                  no_argument, 0, 'q'},
816                         {"test",             required_argument, 0,   1},
817                         {"timeout",          required_argument, 0, 't'},
818                         {"delete-timeout",         no_argument, 0, 'T'},
819                         {"unicode",                no_argument, 0, 'u'},
820                         {"UCS-2",                  no_argument, 0, 'u'},
821                         {"acpi_uid",         required_argument, 0, 'U'},
822                         {"verbose",          optional_argument, 0, 'v'},
823                         {"version",                no_argument, 0, 'V'},
824                         {"write-signature",        no_argument, 0, 'w'},
825                         {"append-binary-args", required_argument, 0, '@'},
826                         {0, 0, 0, 0}
827                 };
828
829                 c = getopt_long (argc, argv,
830                                  "AaBb:cd:e:E:gH:i:l:L:n:No:Op:qt:TuU:v::Vw@:",
831                                  long_options, &option_index);
832                 if (c == -1)
833                         break;
834
835                 switch (c)
836                 {
837                 case '@':
838                         opts.extra_opts_file = optarg;
839                         break;
840                 case 'a':
841                         opts.active = 1;
842                         break;
843                 case 'A':
844                         opts.active = 0;
845                         break;
846                 case 'B':
847                         opts.delete_boot = 1;
848                         break;
849                 case 'b':
850                         rc = sscanf(optarg, "%X", &num);
851                         if (rc == 1) opts.bootnum = num;
852                         break;
853                 case 'c':
854                         opts.create = 1;
855                         break;
856                 case 'd':
857                         opts.disk = optarg;
858                         break;
859                 case 'e':
860                         rc = sscanf(optarg, "%d", &num);
861                         if (rc == 1) opts.edd_version = num;
862                         break;
863                 case 'E':
864                         rc = sscanf(optarg, "%x", &num);
865                         if (rc == 1) opts.edd10_devicenum = num;
866                         break;
867                 case 'g':
868                         opts.forcegpt = 1;
869                         break;
870                 case 'H':
871                         rc = sscanf(optarg, "%x", &num);
872                         if (rc == 1) opts.acpi_hid = num;
873                         break;
874                 case 'i':
875                         opts.iface = optarg;
876                         break;
877                 case 'l':
878                         opts.loader = optarg;
879                         break;
880                 case 'L':
881                         opts.label = optarg;
882                         break;
883                 case 'N':
884                         opts.delete_bootnext = 1;
885                         break;
886                 case 'n':
887                         rc = sscanf(optarg, "%x", &num);
888                         if (rc == 1) opts.bootnext = num;
889                         break;
890                 case 'o':
891                         opts.bootorder = optarg;
892                         break;
893                 case 'O':
894                         opts.delete_bootorder = 1;
895                         break;
896                 case 'p':
897                         rc = sscanf(optarg, "%u", &num);
898                         if (rc == 1) opts.part = num;
899                         break;
900                 case 'q':
901                         opts.quiet = 1;
902                         break;
903                 case 1:
904                         opts.testfile = optarg;
905                         break;
906                 case 't':
907                         rc = sscanf(optarg, "%u", &num);
908                         if (rc == 1) {
909                                 opts.timeout = num;
910                                 opts.set_timeout = 1;
911                         }
912                         break;
913                 case 'T':
914                         opts.delete_timeout = 1;
915                         break;
916                 case 'u':
917                         opts.unicode = 1;
918                         break;
919
920                 case 'U':
921                         rc = sscanf(optarg, "%x", &num);
922                         if (rc == 1) opts.acpi_uid = num;
923                         break;
924                 case 'v':
925                         opts.verbose = 1;
926                         if (optarg) {
927                                 if (!strcmp(optarg, "v"))  opts.verbose = 2;
928                                 if (!strcmp(optarg, "vv")) opts.verbose = 3;
929                                 rc = sscanf(optarg, "%d", &num);
930                                 if (rc == 1)  opts.verbose = num;
931                         }
932                         break;
933                 case 'V':
934                         opts.showversion = 1;
935                         break;
936
937                 case 'w':
938                         opts.write_signature = 1;
939                         break;
940
941                 default:
942                         usage();
943                         exit(1);
944                 }
945         }
946
947         if (optind < argc) {
948                 opts.argc = argc;
949                 opts.argv = argv;
950                 opts.optind = optind;
951         }
952 }
953
954
955 int
956 main(int argc, char **argv)
957 {
958         struct dirent  **boot_names = NULL;
959         var_entry_t *new_boot = NULL;
960         int num, num_boot_names=0;
961
962         set_default_opts();
963         parse_opts(argc, argv);
964         if (opts.showversion) {
965                 printf("version %s\n", EFIBOOTMGR_VERSION);
966                 return 0;
967         }
968
969         if (opts.iface && opts.acpi_hid == -1 && opts.acpi_uid == -1) {
970                 fprintf(stderr, "\nYou must specify the ACPI HID and UID when using -i.\n\n");
971                 return 1;
972         }
973
974         if (!opts.testfile)
975                 set_fs_kernel_calls();
976
977         if (!opts.testfile) {
978                 num_boot_names = read_boot_var_names(&boot_names);
979                 read_vars(boot_names, num_boot_names, &boot_entry_list);
980                 set_var_nums("Boot%04X-%*s", &boot_entry_list);
981
982                 if (opts.delete_boot) {
983                         if (opts.bootnum == -1)
984                                 fprintf(stderr, "\nYou must specify a boot entry to delete (see the -b option).\n\n");
985                         else
986                                 delete_boot_var(opts.bootnum);
987                 }
988
989                 if (opts.active >= 0) {
990                         set_active_state();
991                 }
992         }
993
994         if (opts.create) {
995                 warn_duplicate_name(&boot_entry_list);
996                 new_boot = make_boot_var(&boot_entry_list);
997                 /* Put this boot var in the right BootOrder */
998                 if (!opts.testfile && new_boot)
999                         add_to_boot_order(new_boot->num);
1000         }
1001
1002         if (!opts.testfile) {
1003
1004                 if (opts.delete_bootorder) {
1005                         delete_var("BootOrder");
1006                 }
1007
1008                 if (opts.bootorder) {
1009                         set_boot_order();
1010                 }
1011
1012
1013                 if (opts.delete_bootnext) {
1014                         delete_var("BootNext");
1015                 }
1016
1017                 if (opts.delete_timeout) {
1018                         delete_var("Timeout");
1019                 }
1020
1021                 if (opts.bootnext >= 0) {
1022                         set_boot_u16("BootNext", opts.bootnext & 0xFFFF);
1023                 }
1024
1025                 if (opts.set_timeout) {
1026                         set_boot_u16("Timeout", opts.timeout);
1027                 }
1028
1029                 if (!opts.quiet) {
1030                         num = read_boot_u16("BootNext");
1031                         if (num != -1 ) {
1032                                 printf("BootNext: %04X\n", num);
1033                         }
1034                         num = read_boot_u16("BootCurrent");
1035                         if (num != -1) {
1036                                 printf("BootCurrent: %04X\n", num);
1037                         }
1038                         num = read_boot_u16("Timeout");
1039                         if (num != -1) {
1040                                 printf("Timeout: %u seconds\n", num);
1041                         }
1042                         show_boot_order();
1043                         show_boot_vars();
1044                 }
1045         }
1046         free_dirents(boot_names, num_boot_names);
1047         free_vars(&boot_entry_list);
1048         return 0;
1049 }
1050