Imported Debian patch 0.5.3-1
[debian/efibootmgr] / src / efibootmgr / efibootmgr.c.orig
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 }
766
767 static void
768 set_default_opts()
769 {
770         memset(&opts, 0, sizeof(opts));
771         opts.bootnum         = -1;   /* auto-detect */
772         opts.bootnext        = -1;   /* Don't set it */
773         opts.active          = -1;   /* Don't set it */
774         opts.timeout         = -1;   /* Don't set it */
775         opts.edd10_devicenum = 0x80;
776         opts.loader          = "\\elilo.efi";
777         opts.label           = "Linux";
778         opts.disk            = "/dev/sda";
779         opts.iface           = NULL;
780         opts.part            = 1;
781         opts.acpi_hid        = -1;
782         opts.acpi_uid        = -1;
783 }
784
785 static void
786 parse_opts(int argc, char **argv)
787 {
788         int c, num, rc;
789         int option_index = 0;
790
791         while (1)
792         {
793                 static struct option long_options[] =
794                         /* name, has_arg, flag, val */
795                 {
796                         {"active",                 no_argument, 0, 'a'},
797                         {"inactive",               no_argument, 0, 'A'},
798                         {"bootnum",          required_argument, 0, 'b'},
799                         {"delete-bootnum",         no_argument, 0, 'B'},
800                         {"create",                 no_argument, 0, 'c'},
801                         {"disk",             required_argument, 0, 'd'},
802                         {"iface",            required_argument, 0, 'i'},
803                         {"acpi_hid",         required_argument, 0, 'H'},
804                         {"edd-device",       required_argument, 0, 'E'},
805                         {"edd30",            required_argument, 0, 'e'},
806                         {"gpt",                    no_argument, 0, 'g'},
807                         {"loader",           required_argument, 0, 'l'},
808                         {"label",            required_argument, 0, 'L'},
809                         {"bootnext",         required_argument, 0, 'n'},
810                         {"delete-bootnext",        no_argument, 0, 'N'},
811                         {"bootorder",        required_argument, 0, 'o'},
812                         {"delete-bootorder",       no_argument, 0, 'O'},
813                         {"part",             required_argument, 0, 'p'},
814                         {"quiet",                  no_argument, 0, 'q'},
815                         {"test",             required_argument, 0,   1},
816                         {"timeout",          required_argument, 0, 't'},
817                         {"delete-timeout",         no_argument, 0, 'T'},
818                         {"unicode",                no_argument, 0, 'u'},
819                         {"UCS-2",                  no_argument, 0, 'u'},
820                         {"acpi_uid",         required_argument, 0, 'U'},
821                         {"verbose",          optional_argument, 0, 'v'},
822                         {"version",                no_argument, 0, 'V'},
823                         {"write-signature",        no_argument, 0, 'w'},
824                         {0, 0, 0, 0}
825                 };
826
827                 c = getopt_long (argc, argv,
828                                  "AaBb:cd:e:E:gH:i:l:L:n:No:Op:qt:TuU:v::Vw",
829                                  long_options, &option_index);
830                 if (c == -1)
831                         break;
832
833                 switch (c)
834                 {
835                 case 'a':
836                         opts.active = 1;
837                         break;
838                 case 'A':
839                         opts.active = 0;
840                         break;
841                 case 'B':
842                         opts.delete_boot = 1;
843                         break;
844                 case 'b':
845                         rc = sscanf(optarg, "%X", &num);
846                         if (rc == 1) opts.bootnum = num;
847                         break;
848                 case 'c':
849                         opts.create = 1;
850                         break;
851                 case 'd':
852                         opts.disk = optarg;
853                         break;
854                 case 'e':
855                         rc = sscanf(optarg, "%d", &num);
856                         if (rc == 1) opts.edd_version = num;
857                         break;
858                 case 'E':
859                         rc = sscanf(optarg, "%x", &num);
860                         if (rc == 1) opts.edd10_devicenum = num;
861                         break;
862                 case 'g':
863                         opts.forcegpt = 1;
864                         break;
865                 case 'H':
866                         rc = sscanf(optarg, "%x", &num);
867                         if (rc == 1) opts.acpi_hid = num;
868                         break;
869                 case 'i':
870                         opts.iface = optarg;
871                         break;
872                 case 'l':
873                         opts.loader = optarg;
874                         break;
875                 case 'L':
876                         opts.label = optarg;
877                         break;
878                 case 'N':
879                         opts.delete_bootnext = 1;
880                         break;
881                 case 'n':
882                         rc = sscanf(optarg, "%x", &num);
883                         if (rc == 1) opts.bootnext = num;
884                         break;
885                 case 'o':
886                         opts.bootorder = optarg;
887                         break;
888                 case 'O':
889                         opts.delete_bootorder = 1;
890                         break;
891                 case 'p':
892                         rc = sscanf(optarg, "%u", &num);
893                         if (rc == 1) opts.part = num;
894                         break;
895                 case 'q':
896                         opts.quiet = 1;
897                         break;
898                 case 1:
899                         opts.testfile = optarg;
900                         break;
901                 case 't':
902                         rc = sscanf(optarg, "%u", &num);
903                         if (rc == 1) {
904                                 opts.timeout = num;
905                                 opts.set_timeout = 1;
906                         }
907                         break;
908                 case 'T':
909                         opts.delete_timeout = 1;
910                         break;
911                 case 'u':
912                         opts.unicode = 1;
913                         break;
914
915                 case 'U':
916                         rc = sscanf(optarg, "%x", &num);
917                         if (rc == 1) opts.acpi_uid = num;
918                         break;
919                 case 'v':
920                         opts.verbose = 1;
921                         if (optarg) {
922                                 if (!strcmp(optarg, "v"))  opts.verbose = 2;
923                                 if (!strcmp(optarg, "vv")) opts.verbose = 3;
924                                 rc = sscanf(optarg, "%d", &num);
925                                 if (rc == 1)  opts.verbose = num;
926                         }
927                         break;
928                 case 'V':
929                         opts.showversion = 1;
930                         break;
931
932                 case 'w':
933                         opts.write_signature = 1;
934                         break;
935
936                 default:
937                         usage();
938                         exit(1);
939                 }
940         }
941
942         if (optind < argc) {
943                 opts.argc = argc;
944                 opts.argv = argv;
945                 opts.optind = optind;
946         }
947 }
948
949
950 int
951 main(int argc, char **argv)
952 {
953         struct dirent  **boot_names = NULL;
954         var_entry_t *new_boot = NULL;
955         int num, num_boot_names=0;
956
957         set_default_opts();
958         parse_opts(argc, argv);
959         if (opts.showversion) {
960                 printf("version %s\n", EFIBOOTMGR_VERSION);
961                 return 0;
962         }
963
964         if (opts.iface && opts.acpi_hid == -1 && opts.acpi_uid == -1) {
965                 fprintf(stderr, "\nYou must specify the ACPI HID and UID when using -i.\n\n");
966                 return 1;
967         }
968
969         if (!opts.testfile)
970                 set_fs_kernel_calls();
971
972         if (!opts.testfile) {
973                 num_boot_names = read_boot_var_names(&boot_names);
974                 read_vars(boot_names, num_boot_names, &boot_entry_list);
975                 set_var_nums("Boot%04X-%*s", &boot_entry_list);
976
977                 if (opts.delete_boot) {
978                         if (opts.bootnum == -1)
979                                 fprintf(stderr, "\nYou must specify a boot entry to delete (see the -b option).\n\n");
980                         else
981                                 delete_boot_var(opts.bootnum);
982                 }
983
984                 if (opts.active >= 0) {
985                         set_active_state();
986                 }
987         }
988
989         if (opts.create) {
990                 warn_duplicate_name(&boot_entry_list);
991                 new_boot = make_boot_var(&boot_entry_list);
992                 /* Put this boot var in the right BootOrder */
993                 if (!opts.testfile && new_boot)
994                         add_to_boot_order(new_boot->num);
995         }
996
997         if (!opts.testfile) {
998
999                 if (opts.delete_bootorder) {
1000                         delete_var("BootOrder");
1001                 }
1002
1003                 if (opts.bootorder) {
1004                         set_boot_order();
1005                 }
1006
1007
1008                 if (opts.delete_bootnext) {
1009                         delete_var("BootNext");
1010                 }
1011
1012                 if (opts.delete_timeout) {
1013                         delete_var("Timeout");
1014                 }
1015
1016                 if (opts.bootnext >= 0) {
1017                         set_boot_u16("BootNext", opts.bootnext & 0xFFFF);
1018                 }
1019
1020                 if (opts.set_timeout) {
1021                         set_boot_u16("Timeout", opts.timeout);
1022                 }
1023
1024                 if (!opts.quiet) {
1025                         num = read_boot_u16("BootNext");
1026                         if (num != -1 ) {
1027                                 printf("BootNext: %04X\n", num);
1028                         }
1029                         num = read_boot_u16("BootCurrent");
1030                         if (num != -1) {
1031                                 printf("BootCurrent: %04X\n", num);
1032                         }
1033                         num = read_boot_u16("Timeout");
1034                         if (num != -1) {
1035                                 printf("Timeout: %u seconds\n", num);
1036                         }
1037                         show_boot_order();
1038                         show_boot_vars();
1039                 }
1040         }
1041         free_dirents(boot_names, num_boot_names);
1042         free_vars(&boot_entry_list);
1043         return 0;
1044 }
1045