]> glassweightruler.freedombox.rocks Git - Ventoy.git/blob - GRUB2/MOD_SRC/grub-2.04/grub-core/commands/blscfg.c
update languages.ini (#829 #834)
[Ventoy.git] / GRUB2 / MOD_SRC / grub-2.04 / grub-core / commands / blscfg.c
1 /*-*- Mode: C; c-basic-offset: 2; indent-tabs-mode: t -*-*/
2
3 /* bls.c - implementation of the boot loader spec */
4
5 /*
6 * GRUB -- GRand Unified Bootloader
7 *
8 * GRUB is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * GRUB is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include <grub/list.h>
23 #include <grub/types.h>
24 #include <grub/misc.h>
25 #include <grub/mm.h>
26 #include <grub/err.h>
27 #include <grub/dl.h>
28 #include <grub/extcmd.h>
29 #include <grub/i18n.h>
30 #include <grub/fs.h>
31 #include <grub/env.h>
32 #include <grub/file.h>
33 #include <grub/normal.h>
34 #include <grub/lib/envblk.h>
35
36 #include <stdbool.h>
37
38 GRUB_MOD_LICENSE ("GPLv3+");
39
40 #include "loadenv.h"
41
42 #define GRUB_BLS_CONFIG_PATH "/loader/entries/"
43 #ifdef GRUB_MACHINE_EMU
44 #define GRUB_BOOT_DEVICE "/boot"
45 #else
46 #define GRUB_BOOT_DEVICE "($root)"
47 #endif
48
49 struct keyval
50 {
51 const char *key;
52 char *val;
53 };
54
55 static struct bls_entry *entries = NULL;
56
57 #define FOR_BLS_ENTRIES(var) FOR_LIST_ELEMENTS (var, entries)
58
59 static int bls_add_keyval(struct bls_entry *entry, char *key, char *val)
60 {
61 char *k, *v;
62 struct keyval **kvs, *kv;
63 int new_n = entry->nkeyvals + 1;
64
65 kvs = grub_realloc (entry->keyvals, new_n * sizeof (struct keyval *));
66 if (!kvs)
67 return grub_error (GRUB_ERR_OUT_OF_MEMORY,
68 "couldn't find space for BLS entry");
69 entry->keyvals = kvs;
70
71 kv = grub_malloc (sizeof (struct keyval));
72 if (!kv)
73 return grub_error (GRUB_ERR_OUT_OF_MEMORY,
74 "couldn't find space for BLS entry");
75
76 k = grub_strdup (key);
77 if (!k)
78 {
79 grub_free (kv);
80 return grub_error (GRUB_ERR_OUT_OF_MEMORY,
81 "couldn't find space for BLS entry");
82 }
83
84 v = grub_strdup (val);
85 if (!v)
86 {
87 grub_free (k);
88 grub_free (kv);
89 return grub_error (GRUB_ERR_OUT_OF_MEMORY,
90 "couldn't find space for BLS entry");
91 }
92
93 kv->key = k;
94 kv->val = v;
95
96 entry->keyvals[entry->nkeyvals] = kv;
97 grub_dprintf("blscfg", "new keyval at %p:%s:%s\n", entry->keyvals[entry->nkeyvals], k, v);
98 entry->nkeyvals = new_n;
99
100 return 0;
101 }
102
103 /* Find they value of the key named by keyname. If there are allowed to be
104 * more than one, pass a pointer to an int set to -1 the first time, and pass
105 * the same pointer through each time after, and it'll return them in sorted
106 * order as defined in the BLS fragment file */
107 static char *bls_get_val(struct bls_entry *entry, const char *keyname, int *last)
108 {
109 int idx, start = 0;
110 struct keyval *kv = NULL;
111
112 if (last)
113 start = *last + 1;
114
115 for (idx = start; idx < entry->nkeyvals; idx++) {
116 kv = entry->keyvals[idx];
117
118 if (!grub_strcmp (keyname, kv->key))
119 break;
120 }
121
122 if (idx == entry->nkeyvals) {
123 if (last)
124 *last = -1;
125 return NULL;
126 }
127
128 if (last)
129 *last = idx;
130
131 return kv->val;
132 }
133
134 #define goto_return(x) ({ ret = (x); goto finish; })
135
136 /* compare alpha and numeric segments of two versions */
137 /* return 1: a is newer than b */
138 /* 0: a and b are the same version */
139 /* -1: b is newer than a */
140 static int vercmp(const char * a, const char * b)
141 {
142 char oldch1, oldch2;
143 char *abuf, *bbuf;
144 char *str1, *str2;
145 char * one, * two;
146 int rc;
147 int isnum;
148 int ret = 0;
149
150 grub_dprintf("blscfg", "%s comparing %s and %s\n", __func__, a, b);
151 if (!grub_strcmp(a, b))
152 return 0;
153
154 abuf = grub_malloc(grub_strlen(a) + 1);
155 bbuf = grub_malloc(grub_strlen(b) + 1);
156 str1 = abuf;
157 str2 = bbuf;
158 grub_strcpy(str1, a);
159 grub_strcpy(str2, b);
160
161 one = str1;
162 two = str2;
163
164 /* loop through each version segment of str1 and str2 and compare them */
165 while (*one || *two) {
166 while (*one && !grub_isalnum(*one) && *one != '~') one++;
167 while (*two && !grub_isalnum(*two) && *two != '~') two++;
168
169 /* handle the tilde separator, it sorts before everything else */
170 if (*one == '~' || *two == '~') {
171 if (*one != '~') goto_return (1);
172 if (*two != '~') goto_return (-1);
173 one++;
174 two++;
175 continue;
176 }
177
178 /* If we ran to the end of either, we are finished with the loop */
179 if (!(*one && *two)) break;
180
181 str1 = one;
182 str2 = two;
183
184 /* grab first completely alpha or completely numeric segment */
185 /* leave one and two pointing to the start of the alpha or numeric */
186 /* segment and walk str1 and str2 to end of segment */
187 if (grub_isdigit(*str1)) {
188 while (*str1 && grub_isdigit(*str1)) str1++;
189 while (*str2 && grub_isdigit(*str2)) str2++;
190 isnum = 1;
191 } else {
192 while (*str1 && grub_isalpha(*str1)) str1++;
193 while (*str2 && grub_isalpha(*str2)) str2++;
194 isnum = 0;
195 }
196
197 /* save character at the end of the alpha or numeric segment */
198 /* so that they can be restored after the comparison */
199 oldch1 = *str1;
200 *str1 = '\0';
201 oldch2 = *str2;
202 *str2 = '\0';
203
204 /* this cannot happen, as we previously tested to make sure that */
205 /* the first string has a non-null segment */
206 if (one == str1) goto_return(-1); /* arbitrary */
207
208 /* take care of the case where the two version segments are */
209 /* different types: one numeric, the other alpha (i.e. empty) */
210 /* numeric segments are always newer than alpha segments */
211 /* XXX See patch #60884 (and details) from bugzilla #50977. */
212 if (two == str2) goto_return (isnum ? 1 : -1);
213
214 if (isnum) {
215 grub_size_t onelen, twolen;
216 /* this used to be done by converting the digit segments */
217 /* to ints using atoi() - it's changed because long */
218 /* digit segments can overflow an int - this should fix that. */
219
220 /* throw away any leading zeros - it's a number, right? */
221 while (*one == '0') one++;
222 while (*two == '0') two++;
223
224 /* whichever number has more digits wins */
225 onelen = grub_strlen(one);
226 twolen = grub_strlen(two);
227 if (onelen > twolen) goto_return (1);
228 if (twolen > onelen) goto_return (-1);
229 }
230
231 /* grub_strcmp will return which one is greater - even if the two */
232 /* segments are alpha or if they are numeric. don't return */
233 /* if they are equal because there might be more segments to */
234 /* compare */
235 rc = grub_strcmp(one, two);
236 if (rc) goto_return (rc < 1 ? -1 : 1);
237
238 /* restore character that was replaced by null above */
239 *str1 = oldch1;
240 one = str1;
241 *str2 = oldch2;
242 two = str2;
243 }
244
245 /* this catches the case where all numeric and alpha segments have */
246 /* compared identically but the segment sepparating characters were */
247 /* different */
248 if ((!*one) && (!*two)) goto_return (0);
249
250 /* whichever version still has characters left over wins */
251 if (!*one) goto_return (-1); else goto_return (1);
252
253 finish:
254 grub_free (abuf);
255 grub_free (bbuf);
256 return ret;
257 }
258
259 /* returns name/version/release */
260 /* NULL string pointer returned if nothing found */
261 static void
262 split_package_string (char *package_string, char **name,
263 char **version, char **release)
264 {
265 char *package_version, *package_release;
266
267 /* Release */
268 package_release = grub_strrchr (package_string, '-');
269
270 if (package_release != NULL)
271 *package_release++ = '\0';
272
273 *release = package_release;
274
275 if (name == NULL)
276 {
277 *version = package_string;
278 }
279 else
280 {
281 /* Version */
282 package_version = grub_strrchr(package_string, '-');
283
284 if (package_version != NULL)
285 *package_version++ = '\0';
286
287 *version = package_version;
288 /* Name */
289 *name = package_string;
290 }
291
292 /* Bubble up non-null values from release to name */
293 if (name != NULL && *name == NULL)
294 {
295 *name = (*version == NULL ? *release : *version);
296 *version = *release;
297 *release = NULL;
298 }
299 if (*version == NULL)
300 {
301 *version = *release;
302 *release = NULL;
303 }
304 }
305
306 static int
307 split_cmp(char *nvr0, char *nvr1, int has_name)
308 {
309 int ret = 0;
310 char *name0, *version0, *release0;
311 char *name1, *version1, *release1;
312
313 split_package_string(nvr0, has_name ? &name0 : NULL, &version0, &release0);
314 split_package_string(nvr1, has_name ? &name1 : NULL, &version1, &release1);
315
316 if (has_name)
317 {
318 ret = vercmp(name0 == NULL ? "" : name0,
319 name1 == NULL ? "" : name1);
320 if (ret != 0)
321 return ret;
322 }
323
324 ret = vercmp(version0 == NULL ? "" : version0,
325 version1 == NULL ? "" : version1);
326 if (ret != 0)
327 return ret;
328
329 ret = vercmp(release0 == NULL ? "" : release0,
330 release1 == NULL ? "" : release1);
331 return ret;
332 }
333
334 /* return 1: e0 is newer than e1 */
335 /* 0: e0 and e1 are the same version */
336 /* -1: e1 is newer than e0 */
337 static int bls_cmp(const struct bls_entry *e0, const struct bls_entry *e1)
338 {
339 char *id0, *id1;
340 int r;
341
342 id0 = grub_strdup(e0->filename);
343 id1 = grub_strdup(e1->filename);
344
345 r = split_cmp(id0, id1, 1);
346
347 grub_free(id0);
348 grub_free(id1);
349
350 return r;
351 }
352
353 static void list_add_tail(struct bls_entry *head, struct bls_entry *item)
354 {
355 item->next = head;
356 if (head->prev)
357 head->prev->next = item;
358 item->prev = head->prev;
359 head->prev = item;
360 }
361
362 static int bls_add_entry(struct bls_entry *entry)
363 {
364 struct bls_entry *e, *last = NULL;
365 int rc;
366
367 if (!entries) {
368 grub_dprintf ("blscfg", "Add entry with id \"%s\"\n", entry->filename);
369 entries = entry;
370 return 0;
371 }
372
373 FOR_BLS_ENTRIES(e) {
374 rc = bls_cmp(entry, e);
375
376 if (!rc)
377 return GRUB_ERR_BAD_ARGUMENT;
378
379 if (rc == 1) {
380 grub_dprintf ("blscfg", "Add entry with id \"%s\"\n", entry->filename);
381 list_add_tail (e, entry);
382 if (e == entries) {
383 entries = entry;
384 entry->prev = NULL;
385 }
386 return 0;
387 }
388 last = e;
389 }
390
391 if (last) {
392 grub_dprintf ("blscfg", "Add entry with id \"%s\"\n", entry->filename);
393 last->next = entry;
394 entry->prev = last;
395 }
396
397 return 0;
398 }
399
400 struct read_entry_info {
401 const char *devid;
402 const char *dirname;
403 grub_file_t file;
404 };
405
406 static int read_entry (
407 const char *filename,
408 const struct grub_dirhook_info *dirhook_info UNUSED,
409 void *data)
410 {
411 grub_size_t m = 0, n, clip = 0;
412 int rc = 0;
413 char *p = NULL;
414 grub_file_t f = NULL;
415 struct bls_entry *entry;
416 struct read_entry_info *info = (struct read_entry_info *)data;
417
418 grub_dprintf ("blscfg", "filename: \"%s\"\n", filename);
419
420 n = grub_strlen (filename);
421
422 if (info->file)
423 {
424 f = info->file;
425 }
426 else
427 {
428 if (filename[0] == '.')
429 return 0;
430
431 if (n <= 5)
432 return 0;
433
434 if (grub_strcmp (filename + n - 5, ".conf") != 0)
435 return 0;
436
437 p = grub_xasprintf ("(%s)%s/%s", info->devid, info->dirname, filename);
438
439 f = grub_file_open (p, GRUB_FILE_TYPE_CONFIG);
440 if (!f)
441 goto finish;
442 }
443
444 entry = grub_zalloc (sizeof (*entry));
445 if (!entry)
446 goto finish;
447
448 if (info->file)
449 {
450 char *slash;
451
452 if (n > 5 && !grub_strcmp (filename + n - 5, ".conf") == 0)
453 clip = 5;
454
455 slash = grub_strrchr (filename, '/');
456 if (!slash)
457 slash = grub_strrchr (filename, '\\');
458
459 while (*slash == '/' || *slash == '\\')
460 slash++;
461
462 m = slash ? slash - filename : 0;
463 }
464 else
465 {
466 m = 0;
467 clip = 5;
468 }
469 n -= m;
470
471 entry->filename = grub_strndup(filename + m, n - clip);
472 if (!entry->filename)
473 goto finish;
474
475 entry->filename[n - 5] = '\0';
476
477 for (;;)
478 {
479 char *buf;
480 char *separator;
481
482 buf = grub_file_getline (f);
483 if (!buf)
484 break;
485
486 while (buf && buf[0] && (buf[0] == ' ' || buf[0] == '\t'))
487 buf++;
488 if (buf[0] == '#')
489 continue;
490
491 separator = grub_strchr (buf, ' ');
492
493 if (!separator)
494 separator = grub_strchr (buf, '\t');
495
496 if (!separator || separator[1] == '\0')
497 {
498 grub_free (buf);
499 break;
500 }
501
502 separator[0] = '\0';
503
504 do {
505 separator++;
506 } while (*separator == ' ' || *separator == '\t');
507
508 rc = bls_add_keyval (entry, buf, separator);
509 grub_free (buf);
510 if (rc < 0)
511 break;
512 }
513
514 if (!rc)
515 bls_add_entry(entry);
516
517 finish:
518 if (p)
519 grub_free (p);
520
521 if (f)
522 grub_file_close (f);
523
524 return 0;
525 }
526
527 static grub_envblk_t saved_env = NULL;
528
529 static int UNUSED
530 save_var (const char *name, const char *value, void *whitelist UNUSED)
531 {
532 const char *val = grub_env_get (name);
533 grub_dprintf("blscfg", "saving \"%s\"\n", name);
534
535 if (val)
536 grub_envblk_set (saved_env, name, value);
537
538 return 0;
539 }
540
541 static int UNUSED
542 unset_var (const char *name, const char *value UNUSED, void *whitelist)
543 {
544 grub_dprintf("blscfg", "restoring \"%s\"\n", name);
545 if (! whitelist)
546 {
547 grub_env_unset (name);
548 return 0;
549 }
550
551 if (test_whitelist_membership (name,
552 (const grub_env_whitelist_t *) whitelist))
553 grub_env_unset (name);
554
555 return 0;
556 }
557
558 static char **bls_make_list (struct bls_entry *entry, const char *key, int *num)
559 {
560 int last = -1;
561 char *val;
562
563 int nlist = 0;
564 char **list = NULL;
565
566 list = grub_malloc (sizeof (char *));
567 if (!list)
568 return NULL;
569 list[0] = NULL;
570
571 while (1)
572 {
573 char **new;
574
575 val = bls_get_val (entry, key, &last);
576 if (!val)
577 break;
578
579 new = grub_realloc (list, (nlist + 2) * sizeof (char *));
580 if (!new)
581 break;
582
583 list = new;
584 list[nlist++] = val;
585 list[nlist] = NULL;
586 }
587
588 if (num)
589 *num = nlist;
590
591 return list;
592 }
593
594 static char *field_append(bool is_var, char *buffer, char *start, char *end)
595 {
596 char *temp = grub_strndup(start, end - start + 1);
597 const char *field = temp;
598
599 if (is_var) {
600 field = grub_env_get (temp);
601 if (!field)
602 return buffer;
603 }
604
605 if (!buffer) {
606 buffer = grub_strdup(field);
607 if (!buffer)
608 return NULL;
609 } else {
610 buffer = grub_realloc (buffer, grub_strlen(buffer) + grub_strlen(field));
611 if (!buffer)
612 return NULL;
613
614 grub_stpcpy (buffer + grub_strlen(buffer), field);
615 }
616
617 return buffer;
618 }
619
620 static char *expand_val(char *value)
621 {
622 char *buffer = NULL;
623 char *start = value;
624 char *end = value;
625 bool is_var = false;
626
627 if (!value)
628 return NULL;
629
630 while (*value) {
631 if (*value == '$') {
632 if (start != end) {
633 buffer = field_append(is_var, buffer, start, end);
634 if (!buffer)
635 return NULL;
636 }
637
638 is_var = true;
639 start = value + 1;
640 } else if (is_var) {
641 if (!grub_isalnum(*value) && *value != '_') {
642 buffer = field_append(is_var, buffer, start, end);
643 is_var = false;
644 start = value;
645 }
646 }
647
648 end = value;
649 value++;
650 }
651
652 if (start != end) {
653 buffer = field_append(is_var, buffer, start, end);
654 if (!buffer)
655 return NULL;
656 }
657
658 return buffer;
659 }
660
661 static char **early_initrd_list (const char *initrd)
662 {
663 int nlist = 0;
664 char **list = NULL;
665 char *separator;
666
667 while ((separator = grub_strchr (initrd, ' ')))
668 {
669 list = grub_realloc (list, (nlist + 2) * sizeof (char *));
670 if (!list)
671 return NULL;
672
673 list[nlist++] = grub_strndup(initrd, separator - initrd);
674 list[nlist] = NULL;
675 initrd = separator + 1;
676 }
677
678 list = grub_realloc (list, (nlist + 2) * sizeof (char *));
679 if (!list)
680 return NULL;
681
682 list[nlist++] = grub_strndup(initrd, grub_strlen(initrd));
683 list[nlist] = NULL;
684
685 return list;
686 }
687
688 static void create_entry (struct bls_entry *entry)
689 {
690 int argc = 0;
691 const char **argv = NULL;
692
693 char *title = NULL;
694 char *clinux = NULL;
695 char *options = NULL;
696 char **initrds = NULL;
697 char *initrd = NULL;
698 const char *early_initrd = NULL;
699 char **early_initrds = NULL;
700 char *initrd_prefix = NULL;
701 char *id = entry->filename;
702 char *dotconf = id;
703 char *hotkey = NULL;
704
705 char *users = NULL;
706 char **classes = NULL;
707
708 char **args = NULL;
709
710 char *src = NULL;
711 int bootlen;
712 const char *bootdev;
713 int i, index;
714
715 grub_dprintf("blscfg", "%s got here\n", __func__);
716 clinux = bls_get_val (entry, "linux", NULL);
717 if (!clinux)
718 {
719 grub_dprintf ("blscfg", "Skipping file %s with no 'linux' key.\n", entry->filename);
720 goto finish;
721 }
722
723 bootdev = grub_env_get("ventoy_bls_bootdev");
724 if (!bootdev)
725 {
726 bootdev = GRUB_BOOT_DEVICE;
727 }
728 bootlen = grub_strlen(bootdev) + 2;//space and \0
729
730 /*
731 * strip the ".conf" off the end before we make it our "id" field.
732 */
733 do
734 {
735 dotconf = grub_strstr(dotconf, ".conf");
736 } while (dotconf != NULL && dotconf[5] != '\0');
737 if (dotconf)
738 dotconf[0] = '\0';
739
740 title = bls_get_val (entry, "title", NULL);
741 options = expand_val (bls_get_val (entry, "options", NULL));
742
743 if (!options)
744 options = expand_val ((char *)grub_env_get("default_kernelopts"));
745
746 initrds = bls_make_list (entry, "initrd", NULL);
747
748 hotkey = bls_get_val (entry, "grub_hotkey", NULL);
749 users = expand_val (bls_get_val (entry, "grub_users", NULL));
750 classes = bls_make_list (entry, "grub_class", NULL);
751 args = bls_make_list (entry, "grub_arg", &argc);
752
753 argc += 1;
754 argv = grub_malloc ((argc + 1) * sizeof (char *));
755 argv[0] = title ? title : clinux;
756 for (i = 1; i < argc; i++)
757 argv[i] = args[i-1];
758 argv[argc] = NULL;
759
760 early_initrd = grub_env_get("early_initrd");
761
762 grub_dprintf ("blscfg", "adding menu entry for \"%s\" with id \"%s\"\n",
763 title, id);
764 if (early_initrd)
765 {
766 early_initrds = early_initrd_list(early_initrd);
767 if (!early_initrds)
768 {
769 grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory"));
770 goto finish;
771 }
772
773 if (initrds != NULL && initrds[0] != NULL)
774 {
775 initrd_prefix = grub_strrchr (initrds[0], '/');
776 initrd_prefix = grub_strndup(initrds[0], initrd_prefix - initrds[0] + 1);
777 }
778 else
779 {
780 initrd_prefix = grub_strrchr (clinux, '/');
781 initrd_prefix = grub_strndup(clinux, initrd_prefix - clinux + 1);
782 }
783
784 if (!initrd_prefix)
785 {
786 grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory"));
787 goto finish;
788 }
789 }
790
791 if (early_initrds || initrds)
792 {
793 int initrd_size = sizeof ("initrd");
794 char *tmp;
795
796 for (i = 0; early_initrds != NULL && early_initrds[i] != NULL; i++)
797 initrd_size += bootlen \
798 + grub_strlen(initrd_prefix) \
799 + grub_strlen (early_initrds[i]) + 1;
800
801 for (i = 0; initrds != NULL && initrds[i] != NULL; i++)
802 initrd_size += bootlen \
803 + grub_strlen (initrds[i]) + 1;
804 initrd_size += 1;
805
806 initrd = grub_malloc (initrd_size);
807 if (!initrd)
808 {
809 grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory"));
810 goto finish;
811 }
812
813
814 tmp = grub_stpcpy(initrd, "initrd");
815 for (i = 0; early_initrds != NULL && early_initrds[i] != NULL; i++)
816 {
817 grub_dprintf ("blscfg", "adding early initrd %s\n", early_initrds[i]);
818 tmp = grub_stpcpy (tmp, " ");
819 tmp = grub_stpcpy (tmp, bootdev);
820 tmp = grub_stpcpy (tmp, initrd_prefix);
821 tmp = grub_stpcpy (tmp, early_initrds[i]);
822 grub_free(early_initrds[i]);
823 }
824
825 for (i = 0; initrds != NULL && initrds[i] != NULL; i++)
826 {
827 grub_dprintf ("blscfg", "adding initrd %s\n", initrds[i]);
828 tmp = grub_stpcpy (tmp, " ");
829 tmp = grub_stpcpy (tmp, bootdev);
830 tmp = grub_stpcpy (tmp, initrds[i]);
831 }
832 tmp = grub_stpcpy (tmp, "\n");
833 }
834
835 src = grub_xasprintf ("load_video\n"
836 "set gfxpayload=keep\n"
837 "insmod gzio\n"
838 "linux %s%s%s%s\n"
839 "%s",
840 bootdev, clinux, options ? " " : "", options ? options : "",
841 initrd ? initrd : "");
842
843 grub_normal_add_menu_entry (argc, argv, classes, id, users, hotkey, NULL, src, 0, &index, entry);
844 grub_dprintf ("blscfg", "Added entry %d id:\"%s\"\n", index, id);
845
846 finish:
847 grub_free (initrd);
848 grub_free (initrd_prefix);
849 grub_free (early_initrds);
850 grub_free (initrds);
851 grub_free (options);
852 grub_free (classes);
853 grub_free (args);
854 grub_free (argv);
855 grub_free (src);
856 }
857
858 struct find_entry_info {
859 const char *dirname;
860 const char *devid;
861 grub_device_t dev;
862 grub_fs_t fs;
863 };
864
865 /*
866 * info: the filesystem object the file is on.
867 */
868 static int find_entry (struct find_entry_info *info)
869 {
870 struct read_entry_info read_entry_info;
871 grub_fs_t blsdir_fs = NULL;
872 grub_device_t blsdir_dev = NULL;
873 const char *blsdir = info->dirname;
874 int fallback = 0;
875 int r = 0;
876
877 if (!blsdir) {
878 blsdir = grub_env_get ("blsdir");
879 if (!blsdir)
880 blsdir = GRUB_BLS_CONFIG_PATH;
881 }
882
883 read_entry_info.file = NULL;
884 read_entry_info.dirname = blsdir;
885
886 grub_dprintf ("blscfg", "scanning blsdir: %s\n", blsdir);
887
888 blsdir_dev = info->dev;
889 blsdir_fs = info->fs;
890 read_entry_info.devid = info->devid;
891
892 read_fallback:
893 r = blsdir_fs->fs_dir (blsdir_dev, read_entry_info.dirname, read_entry,
894 &read_entry_info);
895 if (r != 0) {
896 grub_dprintf ("blscfg", "read_entry returned error\n");
897 grub_err_t e;
898 do
899 {
900 e = grub_error_pop();
901 } while (e);
902 }
903
904 if (r && !info->dirname && !fallback) {
905 read_entry_info.dirname = "/boot" GRUB_BLS_CONFIG_PATH;
906 grub_dprintf ("blscfg", "Entries weren't found in %s, fallback to %s\n",
907 blsdir, read_entry_info.dirname);
908 fallback = 1;
909 goto read_fallback;
910 }
911
912 return 0;
913 }
914
915 static grub_err_t
916 bls_load_entries (const char *path)
917 {
918 grub_size_t len;
919 grub_fs_t fs;
920 grub_device_t dev;
921 static grub_err_t r;
922 const char *devid = NULL;
923 char *blsdir = NULL;
924 struct find_entry_info info = {
925 .dev = NULL,
926 .fs = NULL,
927 .dirname = NULL,
928 };
929 struct read_entry_info rei = {
930 .devid = NULL,
931 .dirname = NULL,
932 };
933
934 if (path) {
935 len = grub_strlen (path);
936 if (grub_strcmp (path + len - 5, ".conf") == 0) {
937 rei.file = grub_file_open (path, GRUB_FILE_TYPE_CONFIG);
938 if (!rei.file)
939 return grub_errno;
940 /*
941 * read_entry() closes the file
942 */
943 return read_entry(path, NULL, &rei);
944 } else if (path[0] == '(') {
945 devid = path + 1;
946
947 blsdir = grub_strchr (path, ')');
948 if (!blsdir)
949 return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("Filepath isn't correct"));
950
951 *blsdir = '\0';
952 blsdir = blsdir + 1;
953 }
954 }
955
956 if (!devid) {
957 #ifdef GRUB_MACHINE_EMU
958 devid = "host";
959 #elif defined(GRUB_MACHINE_EFI)
960 devid = grub_env_get ("root");
961 #else
962 devid = grub_env_get ("boot");
963 if (!devid)
964 {
965 devid = grub_env_get ("root");
966 }
967 #endif
968 if (!devid)
969 return grub_error (GRUB_ERR_FILE_NOT_FOUND,
970 N_("variable `%s' isn't set"), "boot");
971 }
972
973 grub_dprintf ("blscfg", "opening %s\n", devid);
974 dev = grub_device_open (devid);
975 if (!dev)
976 return grub_errno;
977
978 grub_dprintf ("blscfg", "probing fs\n");
979 fs = grub_fs_probe (dev);
980 if (!fs)
981 {
982 r = grub_errno;
983 goto finish;
984 }
985
986 info.dirname = blsdir;
987 info.devid = devid;
988 info.dev = dev;
989 info.fs = fs;
990 find_entry(&info);
991
992 finish:
993 if (dev)
994 grub_device_close (dev);
995
996 return r;
997 }
998
999 static bool
1000 is_default_entry(const char *def_entry, struct bls_entry *entry, int idx)
1001 {
1002 const char *title;
1003 int def_idx;
1004
1005 if (!def_entry)
1006 return false;
1007
1008 if (grub_strcmp(def_entry, entry->filename) == 0)
1009 return true;
1010
1011 title = bls_get_val(entry, "title", NULL);
1012
1013 if (title && grub_strcmp(def_entry, title) == 0)
1014 return true;
1015
1016 def_idx = (int)grub_strtol(def_entry, NULL, 0);
1017 if (grub_errno == GRUB_ERR_BAD_NUMBER) {
1018 grub_errno = GRUB_ERR_NONE;
1019 return false;
1020 }
1021
1022 if (def_idx == idx)
1023 return true;
1024
1025 return false;
1026 }
1027
1028 static grub_err_t
1029 bls_create_entries (bool show_default, bool show_non_default, char *entry_id)
1030 {
1031 const char *def_entry = NULL;
1032 struct bls_entry *entry = NULL;
1033 int idx = 0;
1034
1035 def_entry = grub_env_get("default");
1036
1037 grub_dprintf ("blscfg", "%s Creating entries from bls\n", __func__);
1038 FOR_BLS_ENTRIES(entry) {
1039 if (entry->visible) {
1040 idx++;
1041 continue;
1042 }
1043
1044 if ((show_default && is_default_entry(def_entry, entry, idx)) ||
1045 (show_non_default && !is_default_entry(def_entry, entry, idx)) ||
1046 (entry_id && grub_strcmp(entry_id, entry->filename) == 0)) {
1047 create_entry(entry);
1048 entry->visible = 1;
1049 }
1050 idx++;
1051 }
1052
1053 return GRUB_ERR_NONE;
1054 }
1055
1056 static grub_err_t
1057 grub_cmd_blscfg (grub_extcmd_context_t ctxt UNUSED,
1058 int argc, char **args)
1059 {
1060 grub_err_t r;
1061 char *path = NULL;
1062 char *entry_id = NULL;
1063 bool show_default = true;
1064 bool show_non_default = true;
1065
1066 if (argc == 1) {
1067 if (grub_strcmp (args[0], "default") == 0) {
1068 show_non_default = false;
1069 } else if (grub_strcmp (args[0], "non-default") == 0) {
1070 show_default = false;
1071 } else if (args[0][0] == '(') {
1072 path = args[0];
1073 } else {
1074 entry_id = args[0];
1075 show_default = false;
1076 show_non_default = false;
1077 }
1078 }
1079
1080 r = bls_load_entries(path);
1081 if (r)
1082 return r;
1083
1084 return bls_create_entries(show_default, show_non_default, entry_id);
1085 }
1086
1087 static grub_extcmd_t cmd;
1088 static grub_extcmd_t oldcmd;
1089
1090 GRUB_MOD_INIT(blscfg)
1091 {
1092 grub_dprintf("blscfg", "%s got here\n", __func__);
1093 cmd = grub_register_extcmd ("blscfg",
1094 grub_cmd_blscfg,
1095 0,
1096 NULL,
1097 N_("Import Boot Loader Specification snippets."),
1098 NULL);
1099 oldcmd = grub_register_extcmd ("bls_import",
1100 grub_cmd_blscfg,
1101 0,
1102 NULL,
1103 N_("Import Boot Loader Specification snippets."),
1104 NULL);
1105 }
1106
1107 GRUB_MOD_FINI(blscfg)
1108 {
1109 grub_unregister_extcmd (cmd);
1110 grub_unregister_extcmd (oldcmd);
1111 }