From 461affd01f302612d0a573c48f99817f17f39685 Mon Sep 17 00:00:00 2001 From: chengquan Date: Mon, 30 Dec 2019 14:36:43 +0800 Subject: [PATCH] package init --- ...ange-return-type-in-getRootSpecifier.patch | 143 ++++ ...dd-btrfs-subvolume-support-for-grub2.patch | 209 ++++++ 0003-Use-system-LDFLAGS.patch | 25 + 0004-Honor-sbindir.patch | 36 + 0005-installkernel-use-kernel-install.patch | 48 ++ 8.40-1.tar.gz | Bin 0 -> 75067 bytes ...ests-for-various-default-kernel-titl.patch | 182 ++++++ ...ngrubby-debug-after-the-normal-kerne.patch | 123 ++++ ...-about-flushing-our-config-file-when.patch | 78 +++ ...ers-are-not-NULL-before-dereferencin.patch | 39 ++ Don-t-leak-from-one-extractTitle-call.patch | 85 +++ Drop-SEGV-handler.patch | 52 ++ ...temd-debug-settings-on-debug-entries.patch | 28 + ...-about-possible-string-truncations-a.patch | 108 +++ ...ne-options-and-conditionalize-them-t.patch | 64 ++ ...st-case-and-remove-args-with-a-value.patch | 170 +++++ Fix-make-test-fail-for-g2-1.15.patch | 22 + ...t-image-even-if-isn-t-a-suitable-one.patch | 51 ++ README.md | 39 -- ...-env-when-bootloader-is-not-specifie.patch | 30 + drop-uboot-uImage-creation.patch | 232 +++++++ ...ake-test-fail-when-no-boot-partition.patch | 28 + ...-configure-BOOTLOADER-variables-are-.patch | 60 ++ grubby-bls | 616 ++++++++++++++++++ ...y-handle-mixed-and-and-nested-quotes.patch | 162 +++++ grubby.in | 8 + grubby.spec | 122 ++++ installkernel.in | 8 + 28 files changed, 2729 insertions(+), 39 deletions(-) create mode 100644 0001-Change-return-type-in-getRootSpecifier.patch create mode 100644 0002-Add-btrfs-subvolume-support-for-grub2.patch create mode 100644 0003-Use-system-LDFLAGS.patch create mode 100644 0004-Honor-sbindir.patch create mode 100644 0005-installkernel-use-kernel-install.patch create mode 100644 8.40-1.tar.gz create mode 100644 Add-a-bunch-of-tests-for-various-default-kernel-titl.patch create mode 100644 Always-do-the-rungrubby-debug-after-the-normal-kerne.patch create mode 100644 Be-more-thorough-about-flushing-our-config-file-when.patch create mode 100644 Check-that-pointers-are-not-NULL-before-dereferencin.patch create mode 100644 Don-t-leak-from-one-extractTitle-call.patch create mode 100644 Drop-SEGV-handler.patch create mode 100644 Emit-better-systemd-debug-settings-on-debug-entries.patch create mode 100644 Fix-GCC-warnings-about-possible-string-truncations-a.patch create mode 100644 Fix-dracut-cmdline-options-and-conditionalize-them-t.patch create mode 100644 Fix-incorrect-test-case-and-remove-args-with-a-value.patch create mode 100644 Fix-make-test-fail-for-g2-1.15.patch create mode 100644 Print-default-image-even-if-isn-t-a-suitable-one.patch delete mode 100644 README.md create mode 100644 Set-envFile-from-env-when-bootloader-is-not-specifie.patch create mode 100644 drop-uboot-uImage-creation.patch create mode 100644 fix-make-test-fail-when-no-boot-partition.patch create mode 100644 grubby-Make-sure-configure-BOOTLOADER-variables-are-.patch create mode 100644 grubby-bls create mode 100644 grubby-properly-handle-mixed-and-and-nested-quotes.patch create mode 100644 grubby.in create mode 100644 grubby.spec create mode 100644 installkernel.in diff --git a/0001-Change-return-type-in-getRootSpecifier.patch b/0001-Change-return-type-in-getRootSpecifier.patch new file mode 100644 index 0000000..0a6808b --- /dev/null +++ b/0001-Change-return-type-in-getRootSpecifier.patch @@ -0,0 +1,143 @@ +From c1c46d21182974181f5b4c2ed0a02288b4bfd880 Mon Sep 17 00:00:00 2001 +From: Nathaniel McCallum +Date: Fri, 2 Mar 2018 14:59:32 -0500 +Subject: [PATCH 1/3] Change return type in getRootSpecifier() + +Rather than returning a new allocation of the prefix, just return the +length of the prefix. This change accomplishes a couple things. First, +it reduces some memory leaks since the return value was often never +freed. Second, it simplifies the caller who is usually only interested +in the length of the prefix. +--- + grubby.c | 54 +++++++++++++++++++++++++++--------------------------- + 1 file changed, 27 insertions(+), 27 deletions(-) + +diff --git a/grubby.c b/grubby.c +index d4ebb86..a062ef8 100644 +--- a/grubby.c ++++ b/grubby.c +@@ -675,7 +675,7 @@ static int lineWrite(FILE * out, struct singleLine * line, + struct configFileInfo * cfi); + static int getNextLine(char ** bufPtr, struct singleLine * line, + struct configFileInfo * cfi); +-static char * getRootSpecifier(char * str); ++static size_t getRootSpecifier(const char *str); + static void requote(struct singleLine *line, struct configFileInfo * cfi); + static void insertElement(struct singleLine * line, + const char * item, int insertHere, +@@ -1840,7 +1840,7 @@ int suitableImage(struct singleEntry * entry, const char * bootPrefix, + char * fullName; + int i; + char * dev; +- char * rootspec; ++ size_t rs; + char * rootdev; + + if (skipRemoved && entry->skip) { +@@ -1866,12 +1866,11 @@ int suitableImage(struct singleEntry * entry, const char * bootPrefix, + + fullName = alloca(strlen(bootPrefix) + + strlen(line->elements[1].item) + 1); +- rootspec = getRootSpecifier(line->elements[1].item); +- int rootspec_offset = rootspec ? strlen(rootspec) : 0; ++ rs = getRootSpecifier(line->elements[1].item); + int hasslash = endswith(bootPrefix, '/') || +- beginswith(line->elements[1].item + rootspec_offset, '/'); ++ beginswith(line->elements[1].item + rs, '/'); + sprintf(fullName, "%s%s%s", bootPrefix, hasslash ? "" : "/", +- line->elements[1].item + rootspec_offset); ++ line->elements[1].item + rs); + if (access(fullName, R_OK)) { + notSuitablePrintf(entry, 0, "access to %s failed\n", fullName); + return 0; +@@ -1952,7 +1951,6 @@ struct singleEntry * findEntryByPath(struct grubConfig * config, + struct singleLine * line; + int i; + char * chptr; +- char * rootspec = NULL; + enum lineType_e checkType = LT_KERNEL; + + if (isdigit(*kernel)) { +@@ -2044,11 +2042,10 @@ struct singleEntry * findEntryByPath(struct grubConfig * config, + + if (line && line->type != LT_MENUENTRY && + line->numElements >= 2) { +- rootspec = getRootSpecifier(line->elements[1].item); +- if (!strcmp(line->elements[1].item + +- ((rootspec != NULL) ? strlen(rootspec) : 0), +- kernel + strlen(prefix))) +- break; ++ if (!strcmp(line->elements[1].item + ++ getRootSpecifier(line->elements[1].item), ++ kernel + strlen(prefix))) ++ break; + } + if(line->type == LT_MENUENTRY && + !strcmp(line->elements[1].item, kernel)) +@@ -2797,11 +2794,11 @@ struct singleLine * addLineTmpl(struct singleEntry * entry, + + /* but try to keep the rootspec from the template... sigh */ + if (tmplLine->type & (LT_HYPER|LT_KERNEL|LT_MBMODULE|LT_INITRD|LT_KERNEL_EFI|LT_INITRD_EFI|LT_KERNEL_16|LT_INITRD_16)) { +- char * rootspec = getRootSpecifier(tmplLine->elements[1].item); +- if (rootspec != NULL) { +- free(newLine->elements[1].item); +- newLine->elements[1].item = +- sdupprintf("%s%s", rootspec, val); ++ size_t rs = getRootSpecifier(tmplLine->elements[1].item); ++ if (rs > 0) { ++ free(newLine->elements[1].item); ++ newLine->elements[1].item = sdupprintf("%.*s%s", (int) rs, ++ tmplLine->elements[1].item, val); + } + } + } +@@ -3729,15 +3726,19 @@ int checkForElilo(struct grubConfig * config) { + return 1; + } + +-static char * getRootSpecifier(char * str) { +- char * idx, * rootspec = NULL; ++static size_t getRootSpecifier(const char *str) ++{ ++ size_t rs = 0; + + if (*str == '(') { +- idx = rootspec = strdup(str); +- while(*idx && (*idx != ')') && (!isspace(*idx))) idx++; +- *(++idx) = '\0'; ++ for (; str[rs] != ')' && !isspace(str[rs]); rs++) { ++ if (!str[rs]) ++ return rs; ++ } ++ rs++; + } +- return rootspec; ++ ++ return rs; + } + + static char * getInitrdVal(struct grubConfig * config, +@@ -4616,7 +4617,7 @@ int main(int argc, const char ** argv) { + if (displayDefault) { + struct singleLine * line; + struct singleEntry * entry; +- char * rootspec; ++ size_t rs; + + if (config->defaultImage == -1) return 0; + if (config->defaultImage == DEFAULT_SAVED_GRUB2 && +@@ -4629,9 +4630,8 @@ int main(int argc, const char ** argv) { + line = getLineByType(LT_KERNEL|LT_HYPER|LT_KERNEL_EFI|LT_KERNEL_16, entry->lines); + if (!line) return 0; + +- rootspec = getRootSpecifier(line->elements[1].item); +- printf("%s%s\n", bootPrefix, line->elements[1].item + +- ((rootspec != NULL) ? strlen(rootspec) : 0)); ++ rs = getRootSpecifier(line->elements[1].item); ++ printf("%s%s\n", bootPrefix, line->elements[1].item + rs); + + return 0; + +-- +2.14.3 + diff --git a/0002-Add-btrfs-subvolume-support-for-grub2.patch b/0002-Add-btrfs-subvolume-support-for-grub2.patch new file mode 100644 index 0000000..d460c5d --- /dev/null +++ b/0002-Add-btrfs-subvolume-support-for-grub2.patch @@ -0,0 +1,209 @@ +From 5dec033b19bb5b07a0a136a7357e16c8ca9f5dd6 Mon Sep 17 00:00:00 2001 +From: Nathaniel McCallum +Date: Fri, 2 Mar 2018 08:40:18 -0500 +Subject: [PATCH 2/3] Add btrfs subvolume support for grub2 + +In order to find the subvolume prefix from a given path, we parse +/proc/mounts. In cases where /proc/mounts doesn't contain the +filesystem, the caller can use the --mounts option to specify his own +mounts file. + +Btrfs subvolumes are already supported by grub2 and by grub2-mkconfig. + +Fixes #22 +--- + grubby.c | 148 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- + 1 file changed, 143 insertions(+), 5 deletions(-) + +diff --git a/grubby.c b/grubby.c +index a062ef8..96d252a 100644 +--- a/grubby.c ++++ b/grubby.c +@@ -68,6 +68,8 @@ int isEfi = 0; + + char *saved_command_line = NULL; + ++const char *mounts = "/proc/mounts"; ++ + /* comments get lumped in with indention */ + struct lineElement { + char * item; +@@ -1834,6 +1836,129 @@ static int endswith(const char *s, char c) + return s[slen] == c; + } + ++typedef struct { ++ const char *start; ++ size_t chars; ++} field; ++ ++static int iscomma(int c) ++{ ++ return c == ','; ++} ++ ++static int isequal(int c) ++{ ++ return c == '='; ++} ++ ++static field findField(const field *in, typeof(isspace) *isdelim, field *out) ++{ ++ field nxt = {}; ++ size_t off = 0; ++ ++ while (off < in->chars && isdelim(in->start[off])) ++ off++; ++ ++ if (off == in->chars) ++ return nxt; ++ ++ out->start = &in->start[off]; ++ out->chars = 0; ++ ++ while (off + out->chars < in->chars && !isdelim(out->start[out->chars])) ++ out->chars++; ++ ++ nxt.start = out->start + out->chars; ++ nxt.chars = in->chars - off - out->chars; ++ return nxt; ++} ++ ++static int fieldEquals(const field *in, const char *str) ++{ ++ return in->chars == strlen(str) && ++ strncmp(in->start, str, in->chars) == 0; ++} ++ ++/* Parse /proc/mounts to determine the subvolume prefix. */ ++static size_t subvolPrefix(const char *str) ++{ ++ FILE *file = NULL; ++ char *line = NULL; ++ size_t prfx = 0; ++ size_t size = 0; ++ ++ file = fopen(mounts, "r"); ++ if (!file) ++ return 0; ++ ++ for (ssize_t s; (s = getline(&line, &size, file)) >= 0; ) { ++ field nxt = { line, s }; ++ field dev = {}; ++ field path = {}; ++ field type = {}; ++ field opts = {}; ++ field opt = {}; ++ ++ nxt = findField(&nxt, isspace, &dev); ++ if (!nxt.start) ++ continue; ++ ++ nxt = findField(&nxt, isspace, &path); ++ if (!nxt.start) ++ continue; ++ ++ nxt = findField(&nxt, isspace, &type); ++ if (!nxt.start) ++ continue; ++ ++ nxt = findField(&nxt, isspace, &opts); ++ if (!nxt.start) ++ continue; ++ ++ if (!fieldEquals(&type, "btrfs")) ++ continue; ++ ++ /* We have found a btrfs mount point. */ ++ ++ nxt = opts; ++ while ((nxt = findField(&nxt, iscomma, &opt)).start) { ++ field key = {}; ++ field val = {}; ++ ++ opt = findField(&opt, isequal, &key); ++ if (!opt.start) ++ continue; ++ ++ opt = findField(&opt, isequal, &val); ++ if (!opt.start) ++ continue; ++ ++ if (!fieldEquals(&key, "subvol")) ++ continue; ++ ++ /* We have found a btrfs subvolume mount point. */ ++ ++ if (strncmp(val.start, str, val.chars)) ++ continue; ++ ++ if (val.start[val.chars - 1] != '/' && ++ str[val.chars] != '/') ++ continue; ++ ++ /* The subvolume mount point matches our input. */ ++ ++ if (prfx < val.chars) ++ prfx = val.chars; ++ } ++ } ++ ++ dbgPrintf("%s(): str: '%s', prfx: '%s'\n", __FUNCTION__, str, prfx); ++ ++ fclose(file); ++ free(line); ++ return prfx; ++} ++ + int suitableImage(struct singleEntry * entry, const char * bootPrefix, + int skipRemoved, int flags) { + struct singleLine * line; +@@ -2794,12 +2919,22 @@ struct singleLine * addLineTmpl(struct singleEntry * entry, + + /* but try to keep the rootspec from the template... sigh */ + if (tmplLine->type & (LT_HYPER|LT_KERNEL|LT_MBMODULE|LT_INITRD|LT_KERNEL_EFI|LT_INITRD_EFI|LT_KERNEL_16|LT_INITRD_16)) { +- size_t rs = getRootSpecifier(tmplLine->elements[1].item); ++ const char *prfx = tmplLine->elements[1].item; ++ size_t rs = getRootSpecifier(prfx); ++ if (isinitrd(tmplLine->type)) { ++ for (struct singleLine *l = entry->lines; ++ rs == 0 && l; l = l->next) { ++ if (iskernel(l->type)) { ++ prfx = l->elements[1].item; ++ rs = getRootSpecifier(prfx); ++ } ++ } ++ } + if (rs > 0) { + free(newLine->elements[1].item); +- newLine->elements[1].item = sdupprintf("%.*s%s", (int) rs, +- tmplLine->elements[1].item, val); +- } ++ newLine->elements[1].item = sdupprintf("%.*s%s", ++ (int) rs, prfx, val); ++ } + } + } + +@@ -3738,7 +3873,7 @@ static size_t getRootSpecifier(const char *str) + rs++; + } + +- return rs; ++ return rs + subvolPrefix(str + rs); + } + + static char * getInitrdVal(struct grubConfig * config, +@@ -4253,6 +4388,9 @@ int main(int argc, const char ** argv) { + { "mbargs", 0, POPT_ARG_STRING, &newMBKernelArgs, 0, + _("default arguments for the new multiboot kernel or " + "new arguments for multiboot kernel being updated"), NULL }, ++ { "mounts", 0, POPT_ARG_STRING, &mounts, 0, ++ _("path to fake /proc/mounts file (for testing only)"), ++ _("mounts") }, + { "bad-image-okay", 0, 0, &badImageOkay, 0, + _("don't sanity check images in boot entries (for testing only)"), + NULL }, +-- +2.14.3 + diff --git a/0003-Use-system-LDFLAGS.patch b/0003-Use-system-LDFLAGS.patch new file mode 100644 index 0000000..f186b98 --- /dev/null +++ b/0003-Use-system-LDFLAGS.patch @@ -0,0 +1,25 @@ +From fbc4d4feef66df7224fde64adae95525e73bf141 Mon Sep 17 00:00:00 2001 +From: Rafael dos Santos +Date: Tue, 29 May 2018 15:15:24 +0200 +Subject: [PATCH] Use system LDFLAGS + +--- + Makefile | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/Makefile b/Makefile +index ac14404..f0d1372 100644 +--- a/Makefile ++++ b/Makefile +@@ -25,7 +25,7 @@ OBJECTS = grubby.o log.o + CC = gcc + RPM_OPT_FLAGS ?= -O2 -g -pipe -Wp,-D_FORTIFY_SOURCE=2 -fstack-protector + CFLAGS += $(RPM_OPT_FLAGS) -std=gnu99 -Wall -Werror -Wno-error=unused-function -Wno-unused-function -ggdb +-LDFLAGS := ++LDFLAGS := $(RPM_LD_FLAGS) + + grubby_LIBS = -lblkid -lpopt + +-- +2.17.0 + diff --git a/0004-Honor-sbindir.patch b/0004-Honor-sbindir.patch new file mode 100644 index 0000000..5a2c5cf --- /dev/null +++ b/0004-Honor-sbindir.patch @@ -0,0 +1,36 @@ +From a56df998177574ef2db332220c15f11bccd98f7e Mon Sep 17 00:00:00 2001 +From: Peter Jones +Date: Wed, 18 Jul 2018 13:41:02 -0400 +Subject: [PATCH] Honor sbindir + +Signed-off-by: Peter Jones +--- + Makefile | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/Makefile b/Makefile +index ac144046133..2b18dd6404b 100644 +--- a/Makefile ++++ b/Makefile +@@ -42,14 +42,14 @@ test: all + @./test.sh + + install: all +- mkdir -p $(DESTDIR)$(PREFIX)/sbin ++ mkdir -p $(DESTDIR)$(PREFIX)$(sbindir) + mkdir -p $(DESTDIR)/$(mandir)/man8 +- install -m 755 new-kernel-pkg $(DESTDIR)$(PREFIX)/sbin ++ install -m 755 new-kernel-pkg $(DESTDIR)$(PREFIX)$(sbindir) + install -m 644 new-kernel-pkg.8 $(DESTDIR)/$(mandir)/man8 +- install -m 755 installkernel $(DESTDIR)$(PREFIX)/sbin ++ install -m 755 installkernel $(DESTDIR)$(PREFIX)$(sbindir) + install -m 644 installkernel.8 $(DESTDIR)/$(mandir)/man8 + if [ -f grubby ]; then \ +- install -m 755 grubby $(DESTDIR)$(PREFIX)/sbin ; \ ++ install -m 755 grubby $(DESTDIR)$(PREFIX)$(sbindir) ; \ + install -m 644 grubby.8 $(DESTDIR)/$(mandir)/man8 ; \ + fi + +-- +2.17.1 + diff --git a/0005-installkernel-use-kernel-install.patch b/0005-installkernel-use-kernel-install.patch new file mode 100644 index 0000000..2ec577d --- /dev/null +++ b/0005-installkernel-use-kernel-install.patch @@ -0,0 +1,48 @@ +From f93a35be5bdec17044dd2a17980689d3cbf73d58 Mon Sep 17 00:00:00 2001 +From: Javier Martinez Canillas +Date: Tue, 31 Jul 2018 17:43:53 +0200 +Subject: [PATCH] Make installkernel to use kernel-install scripts on BLS + configuration + +The kernel make install target executes the arch/$ARCH/boot/install.sh +that in turns executes the distro specific installkernel script. This +script always uses new-kernel-pkg to install the kernel images. + +But on a BootLoaderSpec setup, the kernel-install scripts must be used +instead. Check if the system uses a BLS setup, and call kernel-install +add in that case instead of new-kernel-pkg. + +Reported-by: Hans de Goede +Signed-off-by: Javier Martinez Canillas +--- + installkernel | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/installkernel b/installkernel +index b887929c179..68dcfac16d2 100755 +--- a/installkernel ++++ b/installkernel +@@ -20,6 +20,8 @@ + # Author(s): tyson@rwii.com + # + ++[[ -f /etc/default/grub ]] && . /etc/default/grub ++ + usage() { + echo "Usage: `basename $0` " >&2 + exit 1 +@@ -77,6 +79,11 @@ cp $MAPFILE $INSTALL_PATH/System.map-$KERNEL_VERSION + ln -fs ${RELATIVE_PATH}$INSTALL_PATH/$KERNEL_NAME-$KERNEL_VERSION $LINK_PATH/$KERNEL_NAME + ln -fs ${RELATIVE_PATH}$INSTALL_PATH/System.map-$KERNEL_VERSION $LINK_PATH/System.map + ++if [ "x${GRUB_ENABLE_BLSCFG}" = "xtrue" ] || [ ! -f /sbin/new-kernel-pkg ]; then ++ kernel-install add $KERNEL_VERSION $INSTALL_PATH/$KERNEL_NAME-$KERNEL_VERSION ++ exit $? ++fi ++ + if [ -n "$cfgLoader" ] && [ -x /sbin/new-kernel-pkg ]; then + if [ -n "$(which dracut 2>/dev/null)" ]; then + new-kernel-pkg --mkinitrd --dracut --host-only --depmod --install --kernel-name $KERNEL_NAME $KERNEL_VERSION +-- +2.17.1 + diff --git a/8.40-1.tar.gz b/8.40-1.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..4cae317a95fbca6af9f24a7c8fde7b83a84d142c GIT binary patch literal 75067 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.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 +Date: Thu, 2 Jul 2015 12:34:30 -0400 +Subject: [PATCH 07/60] Add a bunch of tests for various + --default-{kernel,title,index}. + +... and fix some failures where we see them. + +Related: rhbz#1184014 + +(though I can't actually replicate his failure.) + +Signed-off-by: Peter Jones +--- + grubby.c | 50 ++++++++++++++++++++++++----------------- + test.sh | 10 +++++++++ + test/results/defaultkernel/g.1 | 1 + + test/results/defaultkernel/l1.1 | 1 + + test/results/defaultkernel/z.1 | 1 + + test/results/defaulttitle/z.1 | 1 + + 6 files changed, 44 insertions(+), 20 deletions(-) + create mode 100644 test/results/defaultkernel/g.1 + create mode 100644 test/results/defaultkernel/l1.1 + create mode 100644 test/results/defaultkernel/z.1 + create mode 100644 test/results/defaulttitle/z.1 + +diff --git a/grubby.c b/grubby.c +index 649597e..0bb4869 100644 +--- a/grubby.c ++++ b/grubby.c +@@ -428,7 +428,7 @@ char *grub2ExtractTitle(struct singleLine * line) { + + /* bail out if line does not start with menuentry */ + if (strcmp(line->elements[0].item, "menuentry")) +- return NULL; ++ return NULL; + + i = 1; + current = line->elements[i].item; +@@ -437,10 +437,12 @@ char *grub2ExtractTitle(struct singleLine * line) { + /* if second word is quoted, strip the quotes and return single word */ + if (isquote(*current) && isquote(current[current_len - 1])) { + char *tmp; +- +- tmp = strdup(current); +- *(tmp + current_len - 1) = '\0'; +- return ++tmp; ++ ++ tmp = strdup(current+1); ++ if (!tmp) ++ return NULL; ++ tmp[strlen(tmp)-1] = '\0'; ++ return tmp; + } + + /* if no quotes, return second word verbatim */ +@@ -453,11 +455,11 @@ char *grub2ExtractTitle(struct singleLine * line) { + char * result; + /* need to ensure that ' does not match " as we search */ + char quote_char = *current; +- ++ + resultMaxSize = sizeOfSingleLine(line); + result = malloc(resultMaxSize); + snprintf(result, resultMaxSize, "%s", ++current); +- ++ + i++; + for (; i < line->numElements; ++i) { + current = line->elements[i].item; +@@ -4648,27 +4650,35 @@ int main(int argc, const char ** argv) { + struct singleLine * line; + struct singleEntry * entry; + +- if (config->defaultImage == -1) return 0; ++ if (config->defaultImage == -1) ++ return 0; + if (config->defaultImage == DEFAULT_SAVED_GRUB2 && + cfi->defaultIsSaved) + config->defaultImage = 0; + entry = findEntryByIndex(config, config->defaultImage); +- if (!entry) return 0; ++ if (!entry) ++ return 0; + + if (!configureGrub2) { +- line = getLineByType(LT_TITLE, entry->lines); +- if (!line) return 0; +- printf("%s\n", line->elements[1].item); +- ++ char *title; ++ line = getLineByType(LT_TITLE, entry->lines); ++ if (!line) ++ return 0; ++ title = extractTitle(config, line); ++ if (!title) ++ return 0; ++ printf("%s\n", title); ++ free(title); + } else { +- char * title; ++ char * title; + +- dbgPrintf("This is GRUB2, default title is embeded in menuentry\n"); +- line = getLineByType(LT_MENUENTRY, entry->lines); +- if (!line) return 0; +- title = grub2ExtractTitle(line); +- if (title) +- printf("%s\n", title); ++ dbgPrintf("This is GRUB2, default title is embeded in menuentry\n"); ++ line = getLineByType(LT_MENUENTRY, entry->lines); ++ if (!line) ++ return 0; ++ title = grub2ExtractTitle(line); ++ if (title) ++ printf("%s\n", title); + } + return 0; + +diff --git a/test.sh b/test.sh +index 6379698..96e0087 100755 +--- a/test.sh ++++ b/test.sh +@@ -298,6 +298,9 @@ grubDisplayTest grub.9 defaulttitle/g.9 --default-title + grubDisplayTest grub.10 defaulttitle/g.10 --default-title + grubDisplayTest grub.11 defaulttitle/g.11 --default-title + ++testing="GRUB display default kernel" ++grubDisplayTest grub.1 defaultkernel/g.1 --default-kernel ++ + testing="LILO default directive" + liloTest lilo.1 default/l1.1 --set-default=/boot/vmlinuz-2.4.18-4 + liloTest lilo.1 default/l1.2 --remove-kernel=/boot/vmlinuz-2.4.18-4smp +@@ -305,10 +308,17 @@ liloTest lilo.1 default/l1.3 --add-kernel /boot/kernel --title label \ + --copy-default + liloTest lilo.1 default/l1.4 --add-kernel /boot/kernel --title label \ + --copy-default --make-default ++liloDisplayTest lilo.1 defaultkernel/l1.1 --default-kernel + + testing="Z/IPL default directive" + ziplTest zipl.1 default/z1.1 --add-kernel /boot/new-kernel --title test + ziplTest zipl.1 default/z1.2 --add-kernel /boot/new-kernel --title test --make-default ++testing="Z/IPL display default index" ++ziplDisplayTest zipl.1 defaultindex/0 --default-index ++testing="Z/IPL display default title" ++ziplDisplayTest zipl.1 defaulttitle/z.1 --default-title ++testing="Z/IPL display default kernel" ++ziplDisplayTest zipl.1 defaultkernel/z.1 --default-kernel + + testing="GRUB fallback directive" + grubTest grub.5 fallback/g5.1 --remove-kernel=/boot/vmlinuz-2.4.7-ac3 \ +diff --git a/test/results/defaultkernel/g.1 b/test/results/defaultkernel/g.1 +new file mode 100644 +index 0000000..2c3ac11 +--- /dev/null ++++ b/test/results/defaultkernel/g.1 +@@ -0,0 +1 @@ ++/boot/vmlinuz-2.4.7-2 +diff --git a/test/results/defaultkernel/l1.1 b/test/results/defaultkernel/l1.1 +new file mode 100644 +index 0000000..fd22b1b +--- /dev/null ++++ b/test/results/defaultkernel/l1.1 +@@ -0,0 +1 @@ ++/boot/vmlinuz-2.4.18-4smp +diff --git a/test/results/defaultkernel/z.1 b/test/results/defaultkernel/z.1 +new file mode 100644 +index 0000000..2c62e98 +--- /dev/null ++++ b/test/results/defaultkernel/z.1 +@@ -0,0 +1 @@ ++/boot/vmlinuz-2.4.9-37 +diff --git a/test/results/defaulttitle/z.1 b/test/results/defaulttitle/z.1 +new file mode 100644 +index 0000000..a08e1f3 +--- /dev/null ++++ b/test/results/defaulttitle/z.1 +@@ -0,0 +1 @@ ++linux +-- +1.8.3.1 + diff --git a/Always-do-the-rungrubby-debug-after-the-normal-kerne.patch b/Always-do-the-rungrubby-debug-after-the-normal-kerne.patch new file mode 100644 index 0000000..3a7cf18 --- /dev/null +++ b/Always-do-the-rungrubby-debug-after-the-normal-kerne.patch @@ -0,0 +1,123 @@ +From b9a37e249bf279ceb0b63ad4676f03d11796cfc9 Mon Sep 17 00:00:00 2001 +From: Peter Jones +Date: Mon, 26 Oct 2015 14:22:39 -0400 +Subject: [PATCH 18/60] Always do the "rungrubby --debug" after the normal + kernel on install. + +This way the during an update, the right kernel is picked as "default" +for the command line arguments. + +Related: rhbz#1212128 + +Signed-off-by: Peter Jones +--- + new-kernel-pkg | 20 ++++++++++---------- + 1 file changed, 10 insertions(+), 10 deletions(-) + +diff --git a/new-kernel-pkg b/new-kernel-pkg +index 9f56c47..9574dbb 100755 +--- a/new-kernel-pkg ++++ b/new-kernel-pkg +@@ -243,8 +243,8 @@ install() { + --args=\"root=$rootdevice $kernargs \$debugargs\" \ + --remove-kernel=\"TITLE=$title\$debugtitle\"" + +- rungrubby --debug ${ARGS} + rungrubby ${ARGS} $makedefault ++ rungrubby --debug ${ARGS} + else + [ -n "$verbose" ] && echo "$grubConfig does not exist, not running grubby for grub 0.97" + fi +@@ -257,8 +257,8 @@ install() { + ${mbargs:+--mbargs=\"$mbargs\"} \ + --args=\"root=$rootdevice $kernargs \$debugargs\" \ + --remove-kernel=\"TITLE=$title\$debugtitle\"" +- rungrubby --debug ${ARGS} + rungrubby ${ARGS} $makedefault ++ rungrubby --debug ${ARGS} + else + [ -n "$verbose" ] && echo "$grub2Config does not exist, not running grubby for grub 2" + fi +@@ -272,8 +272,8 @@ install() { + ${mbargs:+--mbargs=\"$mbargs\"} \ + --args=\"root=$rootdevice $kernargs \$debugargs\" \ + --remove-kernel=\"TITLE=$title\$debugtitle\"" +- rungrubby --debug ${ARGS} + rungrubby ${ARGS} $makedefault ++ rungrubby --debug ${ARGS} + else + [ -n "$verbose" ] && echo "$grub2EfiConfig does not exist, not running grubby for grub 2 with UEFI" + fi +@@ -288,8 +288,8 @@ install() { + --args=\"root=$rootdevice $kernargs \$debugargs\" \ + --remove-kernel=\"TITLE=$version\"" + +- rungrubby --debug ${ARGS} + rungrubby ${ARGS} $makedefault ++ rungrubby --debug ${ARGS} + if [ -n "$runLilo" ]; then + [ -n "$verbose" ] && echo "running $lilo" + if [ ! -x $lilo ] ; then +@@ -313,8 +313,8 @@ install() { + --args=\"root=$rootdevice $kernargs \$debugargs\" \ + --remove-kernel=\"TITLE=$title\$debugtitle\"" + +- rungrubby --debug ${ARGS} + rungrubby ${ARGS} $makedefault ++ rungrubby --debug ${ARGS} + else + [ -n "$verbose" ] && echo "$extlinuxConfig does not exist, not running grubby for extlinux" + fi +@@ -480,8 +480,8 @@ update() { + ${mbkernel:+--add-multiboot=\"$mbkernel\"} \ + --title=\"$title\$debugtitle\"" + +- rungrubby --debug ${ARGS} + rungrubby ${ARGS} ++ rungrubby --debug ${ARGS} + else + [ -n "$verbose" ] && echo "$grubConfig does not exist, not running grubby" + fi +@@ -493,8 +493,8 @@ update() { + ${removeargs:+--remove-args=\"$removeargs\"} \ + --title=\"$title\$debugtitle\"" + +- rungrubby --debug ${ARGS} + rungrubby ${ARGS} ++ rungrubby --debug ${ARGS} + else + [ -n "$verbose" ] && echo "$grub2Config does not exist, not running grubby" + fi +@@ -506,8 +506,8 @@ update() { + ${removeargs:+--remove-args=\"$removeargs\"} \ + --title=\"$title\$debugtitle\"" + +- rungrubby --debug ${ARGS} + rungrubby ${ARGS} ++ rungrubby --debug ${ARGS} + else + [ -n "$verbose" ] && echo "$grub2EfiConfig does not exist, not running grubby" + fi +@@ -519,8 +519,8 @@ update() { + ${removeargs:+--remove-args=\"$removeargs\"} \ + --title=\"$title\$debugtitle\"" + +- rungrubby --debug ${ARGS} + rungrubby ${ARGS} ++ rungrubby --debug ${ARGS} + + if [ -n "$runLilo" ]; then + [ -n "$verbose" ] && echo "running $lilo" +@@ -571,8 +571,8 @@ update() { + ${removeargs:+--remove-args=\"$removeargs\"} \ + --title=\"$title\$debugtitle\"" + +- rungrubby --debug ${ARGS} + rungrubby ${ARGS} ++ rungrubby --debug ${ARGS} + else + [ -n "$verbose" ] && echo "$extlinuxConfig does not exist, not running grubby" + fi +-- +1.8.3.1 + diff --git a/Be-more-thorough-about-flushing-our-config-file-when.patch b/Be-more-thorough-about-flushing-our-config-file-when.patch new file mode 100644 index 0000000..498f01e --- /dev/null +++ b/Be-more-thorough-about-flushing-our-config-file-when.patch @@ -0,0 +1,78 @@ +From d9406b061cef40bd42ed813e13bd02b3a285adae Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Fran=C3=A7ois=20Cami?= +Date: Fri, 23 Jun 2017 23:56:50 +0200 +Subject: [PATCH 44/60] Be more thorough about flushing our config file +when + writing. + +Add missing fclose() at the end of writeConfig +Use fflush(out) + fsync(fileno(out) on temporary file +fsync() the destination directory after rename +--- + grubby.c | 40 ++++++++++++++++++++++++++++++++++------ + 1 file changed, 34 insertions(+), 6 deletions(-) + +diff --git a/grubby.c b/grubby.c +index 345195c..951abb7 100644 +--- a/grubby.c ++++ b/grubby.c +@@ -1591,6 +1591,7 @@ static int writeConfig(struct grubConfig * cfg, char * outName, + int needs = MAIN_DEFAULT; + struct stat sb; + int i; ++ int rc = 0; + + if (!strcmp(outName, "-")) { + out = stdout; +@@ -1694,15 +1695,42 @@ static int writeConfig(struct grubConfig * cfg, char * outName, + } + + if (tmpOutName) { +- if (rename(tmpOutName, outName)) { +- fprintf(stderr, _("grubby: error moving %s to %s: %s\n"), +- tmpOutName, outName, strerror(errno)); +- unlink(outName); +- return 1; ++ /* write userspace buffers */ ++ if (fflush(out)) ++ rc = 1; ++ ++ /* purge the write-back cache with fsync() */ ++ if (fsync(fileno(out))) ++ rc = 1; ++ ++ if (fclose(out)) ++ rc = 1; ++ ++ if (rc == 0 && rename(tmpOutName, outName)) { ++ unlink(tmpOutName); ++ rc = 1; ++ } ++ ++ /* fsync() the destination directory after rename */ ++ if (rc == 0) { ++ int dirfd; ++ ++ dirfd = open(dirname(strdupa(outName)), O_RDONLY); ++ if (dirfd < 0) ++ rc = 1; ++ else if (fsync(dirfd)) ++ rc = 1; ++ ++ if (dirfd >= 0) ++ close(dirfd); + } ++ ++ if (rc == 1) ++ fprintf(stderr, ++ _("grubby: error flushing data: %m\n")); + } + +- return 0; ++ return rc; + } + + static int numEntries(struct grubConfig *cfg) { +-- +2.19.1 + diff --git a/Check-that-pointers-are-not-NULL-before-dereferencin.patch b/Check-that-pointers-are-not-NULL-before-dereferencin.patch new file mode 100644 index 0000000..b36671b --- /dev/null +++ b/Check-that-pointers-are-not-NULL-before-dereferencin.patch @@ -0,0 +1,39 @@ +From 90e25125a8e26560a8e0fe27462ea639ad0b309b Mon Sep 17 00:00:00 2001 +From: Javier Martinez Canillas +Date: Fri, 15 Mar 2019 10:14:42 +0100 +Subject: [PATCH 59/60] Check that pointers are not NULL before +dereferencing + them + +The coverity scan complains that the argValueMatch() function derefences +the chptra and chptrb pointers when these may be NULL. That's not really +true since the function first checks if both aren't NULL and then only +dereferences one when the other is NULL. + +But still this confuses coverity, so to make it happy let's just check +if the pointer isn't NULL before derefencing them. + +Signed-off-by: Javier Martinez Canillas +--- + grubby.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/grubby.c b/grubby.c +index 1de7b52..522d4d5 100644 +--- a/grubby.c ++++ b/grubby.c +@@ -3340,9 +3340,9 @@ static int argValueMatch(const char *one, const char *two) + + if (!chptra && !chptrb) + return 0; +- else if (!chptra) ++ else if (!chptra && chptrb) + return *chptrb - 0; +- else if (!chptrb) ++ else if (!chptrb && chptra) + return 0 - *chptra; + else + return strcmp(chptra, chptrb); +-- +2.19.1 + diff --git a/Don-t-leak-from-one-extractTitle-call.patch b/Don-t-leak-from-one-extractTitle-call.patch new file mode 100644 index 0000000..d1bc92a --- /dev/null +++ b/Don-t-leak-from-one-extractTitle-call.patch @@ -0,0 +1,85 @@ +From 7713f8e23e326dcf1258a715e2554a4bf53dec59 Mon Sep 17 00:00:00 2001 +From: Peter Jones +Date: Thu, 2 Jul 2015 16:26:59 -0400 +Subject: [PATCH 11/60] Don't leak from one extractTitle() call. + +Found by coverity. + +Signed-off-by: Peter Jones +--- + grubby.c | 27 +++++++++++++++++---------- + 1 file changed, 17 insertions(+), 10 deletions(-) + +diff --git a/grubby.c b/grubby.c +index 0bb4869..70477ba 100644 +--- a/grubby.c ++++ b/grubby.c +@@ -1510,13 +1510,14 @@ static struct grubConfig * readConfig(const char * inName, + return cfg; + } + +-static void writeDefault(FILE * out, char * indent, ++static void writeDefault(FILE * out, char * indent, + char * separator, struct grubConfig * cfg) { + struct singleEntry * entry; + struct singleLine * line; + int i; + +- if (!cfg->defaultImage && cfg->flags == GRUB_CONFIG_NO_DEFAULT) return; ++ if (!cfg->defaultImage && cfg->flags == GRUB_CONFIG_NO_DEFAULT) ++ return; + + if (cfg->defaultImage == DEFAULT_SAVED) + fprintf(out, "%sdefault%ssaved\n", indent, separator); +@@ -1540,34 +1541,40 @@ static void writeDefault(FILE * out, char * indent, + fprintf(out, "%sset default=\"%d\"\n", indent, + cfg->defaultImage); + } else { +- fprintf(out, "%sdefault%s%d\n", indent, separator, ++ fprintf(out, "%sdefault%s%d\n", indent, separator, + cfg->defaultImage); + } + } else { + int image = cfg->defaultImage; + + entry = cfg->entries; +- while (entry && entry->skip) entry = entry->next; ++ while (entry && entry->skip) ++ entry = entry->next; + + i = 0; + while (entry && i < image) { + entry = entry->next; + +- while (entry && entry->skip) entry = entry->next; ++ while (entry && entry->skip) ++ entry = entry->next; + i++; + } + +- if (!entry) return; ++ if (!entry) ++ return; + + line = getLineByType(LT_TITLE, entry->lines); + + if (line && line->numElements >= 2) +- fprintf(out, "%sdefault%s%s\n", indent, separator, ++ fprintf(out, "%sdefault%s%s\n", indent, separator, + line->elements[1].item); +- else if (line && (line->numElements == 1) && ++ else if (line && (line->numElements == 1) && + cfg->cfi->titleBracketed) { +- fprintf(out, "%sdefault%s%s\n", indent, separator, +- extractTitle(cfg, line)); ++ char *title = extractTitle(cfg, line); ++ if (title) { ++ fprintf(out, "%sdefault%s%s\n", indent, separator, title); ++ free(title); ++ } + } + } + } +-- +1.8.3.1 + diff --git a/Drop-SEGV-handler.patch b/Drop-SEGV-handler.patch new file mode 100644 index 0000000..1430a42 --- /dev/null +++ b/Drop-SEGV-handler.patch @@ -0,0 +1,52 @@ +From d1309f34c347bea393582b5d927db1c7c577198c Mon Sep 17 00:00:00 2001 +From: Lubomir Rintel +Date: Thu, 27 Feb 2014 10:35:59 +0100 +Subject: [PATCH 06/60] Drop SEGV handler + +The generated tracebacks are mostly useless without debuginfo (which is likely +not present if the crash is not anticipated) and prevent ABRT from doing a +better job. + +Signed-off-by: Lubomir Rintel +--- + grubby.c | 17 ----------------- + 1 file changed, 17 deletions(-) + +diff --git a/grubby.c b/grubby.c +index 440c627..649597e 100644 +--- a/grubby.c ++++ b/grubby.c +@@ -4211,21 +4211,6 @@ int addNewKernel(struct grubConfig * config, struct singleEntry * template, + return 0; + } + +-static void traceback(int signum) +-{ +- void *array[40]; +- size_t size; +- +- signal(SIGSEGV, SIG_DFL); +- memset(array, '\0', sizeof (array)); +- size = backtrace(array, 40); +- +- fprintf(stderr, "grubby received SIGSEGV! Backtrace (%ld):\n", +- (unsigned long)size); +- backtrace_symbols_fd(array, size, STDERR_FILENO); +- exit(1); +-} +- + int main(int argc, const char ** argv) { + poptContext optCon; + const char * grubConfig = NULL; +@@ -4368,8 +4353,6 @@ int main(int argc, const char ** argv) { + + useextlinuxmenu=0; + +- signal(SIGSEGV, traceback); +- + int i = 0; + for (int j = 1; j < argc; j++) + i += strlen(argv[j]) + 1; +-- +1.8.3.1 + diff --git a/Emit-better-systemd-debug-settings-on-debug-entries.patch b/Emit-better-systemd-debug-settings-on-debug-entries.patch new file mode 100644 index 0000000..206e640 --- /dev/null +++ b/Emit-better-systemd-debug-settings-on-debug-entries.patch @@ -0,0 +1,28 @@ +From 191c79fe21c51ee893e40e2f53c03175286fdb0f Mon Sep 17 00:00:00 2001 +From: Peter Jones +Date: Thu, 2 Jul 2015 12:44:51 -0400 +Subject: [PATCH 08/60] Emit better systemd debug settings on debug entries. + +Resolves: rhbz#1212128 + +Signed-off-by: Peter Jones +--- + new-kernel-pkg | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/new-kernel-pkg b/new-kernel-pkg +index 1cdbbb9..1f6ab39 100755 +--- a/new-kernel-pkg ++++ b/new-kernel-pkg +@@ -121,7 +121,7 @@ mbkernel="$HYPERVISOR" + mbargs="$HYPERVISOR_ARGS" + adddracutargs="" + addplymouthinitrd="" +-DEBUGARG="systemd.debug" ++DEBUGARG="systemd.log_level=debug systemd.log_target=kmsg" + + usage() { + echo "Usage: `basename $0` [-v] [--mkinitrd] [--rminitrd] [--dracut]" >&2 +-- +1.8.3.1 + diff --git a/Fix-GCC-warnings-about-possible-string-truncations-a.patch b/Fix-GCC-warnings-about-possible-string-truncations-a.patch new file mode 100644 index 0000000..346972c --- /dev/null +++ b/Fix-GCC-warnings-about-possible-string-truncations-a.patch @@ -0,0 +1,108 @@ +From 9a2fc457659cc2baee7d13ed6b0f8c864ae605d9 Mon Sep 17 00:00:00 2001 +From: Javier Martinez Canillas +Date: Tue, 5 Feb 2019 17:29:11 +0100 +Subject: [PATCH 58/60] Fix GCC warnings about possible string +truncations and + buffer overflows + +Building with -Werror=stringop-truncation and -Werror=stringop-overflow +leads to GCC complaining about possible string truncation and overflows. + +Fix this by using memcpy(), explicitly calculating the buffers lenghts +and set a NUL byte terminator after copying the buffers. + +Signed-off-by: Javier Martinez Canillas +--- + grubby.c | 38 ++++++++++++++++++++++++++++++-------- + 1 file changed, 30 insertions(+), 8 deletions(-) + +diff --git a/grubby.c b/grubby.c +index 1d3e924..1de7b52 100644 +--- a/grubby.c ++++ b/grubby.c +@@ -463,20 +463,28 @@ char *grub2ExtractTitle(struct singleLine * line) { + snprintf(result, resultMaxSize, "%s", ++current); + + i++; ++ int result_len = 0; + for (; i < line->numElements; ++i) { + current = line->elements[i].item; + current_len = strlen(current); + current_indent = line->elements[i].indent; + current_indent_len = strlen(current_indent); + +- strncat(result, current_indent, current_indent_len); ++ memcpy(result + result_len, current_indent, current_indent_len); ++ result_len += current_indent_len; ++ + if (current[current_len-1] != quote_char) { +- strncat(result, current, current_len); ++ memcpy(result + result_len, current_indent, ++ current_indent_len); ++ result_len += current_len; + } else { +- strncat(result, current, current_len - 1); ++ memcpy(result + result_len, current_indent, ++ current_indent_len); ++ result_len += (current_len - 1); + break; + } + } ++ result[result_len] = '\0'; + return result; + } + +@@ -1300,6 +1308,7 @@ static struct grubConfig * readConfig(const char * inName, + extras = malloc(len + 1); + *extras = '\0'; + ++ int buf_len = 0; + /* get title. */ + for (int i = 0; i < line->numElements; i++) { + if (!strcmp(line->elements[i].item, "menuentry")) +@@ -1314,13 +1323,18 @@ static struct grubConfig * readConfig(const char * inName, + + len = strlen(title); + if (title[len-1] == quote_char) { +- strncat(buf, title,len-1); ++ memcpy(buf + buf_len, title, len - 1); ++ buf_len += (len - 1); + break; + } else { +- strcat(buf, title); +- strcat(buf, line->elements[i].indent); ++ memcpy(buf + buf_len, title, len); ++ buf_len += len; ++ len = strlen(line->elements[i].indent); ++ memcpy(buf + buf_len, line->elements[i].indent, len); ++ buf_len += len; + } + } ++ buf[buf_len] = '\0'; + + /* get extras */ + int count = 0; +@@ -4589,10 +4603,18 @@ int main(int argc, const char ** argv) { + exit(1); + } + saved_command_line[0] = '\0'; ++ int cmdline_len = 0, arg_len; + for (int j = 1; j < argc; j++) { +- strcat(saved_command_line, argv[j]); +- strncat(saved_command_line, j == argc -1 ? "" : " ", 1); ++ arg_len = strlen(argv[j]); ++ memcpy(saved_command_line + cmdline_len, argv[j], arg_len); ++ cmdline_len += arg_len; ++ if (j != argc - 1) { ++ memcpy(saved_command_line + cmdline_len, " ", 1); ++ cmdline_len++; ++ } ++ + } ++ saved_command_line[cmdline_len] = '\0'; + + optCon = poptGetContext("grubby", argc, argv, options, 0); + poptReadDefaultConfig(optCon, 1); +-- +2.19.1 + diff --git a/Fix-dracut-cmdline-options-and-conditionalize-them-t.patch b/Fix-dracut-cmdline-options-and-conditionalize-them-t.patch new file mode 100644 index 0000000..c99cdaa --- /dev/null +++ b/Fix-dracut-cmdline-options-and-conditionalize-them-t.patch @@ -0,0 +1,64 @@ +From a6843cb0fc122a3bd8a7e4067c3783ef4ce69f33 Mon Sep 17 00:00:00 2001 +From: marcosfrm +Date: Tue, 6 Oct 2015 08:29:02 -0300 +Subject: [PATCH 16/60] Fix dracut cmdline options and conditionalize them to + --add-dracut-args + +By default initramfs generated by dracut is HostOnly and has vconsole.conf and locale.conf included. Instead of killing this import section, conditionalize it to --add-dracut-args. + +Reference: http://git.kernel.org/cgit/boot/dracut/dracut.git/tree/modules.d/10i18n/parse-i18n.sh +--- + new-kernel-pkg | 37 +++++++++++++++---------------------- + 1 file changed, 15 insertions(+), 22 deletions(-) + +diff --git a/new-kernel-pkg b/new-kernel-pkg +index 90652da..997fb1f 100755 +--- a/new-kernel-pkg ++++ b/new-kernel-pkg +@@ -825,28 +825,21 @@ if [[ ${ARCH} =~ armv[5|7].*l ]]; then + fi + [ -n "$verbose" ] && echo "devtreedir is $devtreedir" + +-# add dracut i18n, keyboard and plymouth kernel args if requested +-if [ -n "$dracut" -o -n "$adddracutargs" ]; then +- if [ -r /etc/vconsole.conf ]; then +- . /etc/vconsole.conf +- elif [ -r /etc/sysconfig/keyboard ]; then +- . /etc/sysconfig/keyboard +- fi +- +- if [ -r /etc/locale.conf ]; then +- . /etc/locale.conf +- elif [ -r /etc/sysconfig/i18n ]; then +- . /etc/sysconfig/i18n +- fi +- +- for i in SYSFONT SYSFONTACM UNIMAP LANG KEYTABLE; do +- val=$(eval echo \$$i) +- [ -n "$val" ] && kernargs="$kernargs $i=$val" +- done +- +- if [ -n "$KEYBOARDTYPE" -a "$KEYBOARDTYPE" != "pc" ]; then +- kernargs="$kernargs KEYBOARDTYPE=$KEYBOARDTYPE" +- fi ++# add dracut kernel args if requested ++if [ -n "$dracut" -a -n "$adddracutargs" ]; then ++ [ -r /etc/vconsole.conf ] && . /etc/vconsole.conf ++ [ -r /etc/locale.conf ] && . /etc/locale.conf ++ ++ while read opt rd_opt; do ++ [ -n "${!opt}" ] && kernargs="$kernargs $rd_opt=\"${!opt}\"" ++ done <<< 'KEYMAP rd.vconsole.keymap ++ FONT rd.vconsole.font ++ FONT_MAP rd.vconsole.font.map ++ FONT_UNIMAP rd.vconsole.font.unimap ++ UNICODE rd.vconsole.font.unicode ++ EXT_KEYMAP rd.vconsole.keymap.ext ++ LANG rd.locale.LANG ++ LC_ALL rd.locale.LC_ALL' + fi + + # set this as the default if we have the package and it matches +-- +1.8.3.1 + diff --git a/Fix-incorrect-test-case-and-remove-args-with-a-value.patch b/Fix-incorrect-test-case-and-remove-args-with-a-value.patch new file mode 100644 index 0000000..521b0e5 --- /dev/null +++ b/Fix-incorrect-test-case-and-remove-args-with-a-value.patch @@ -0,0 +1,170 @@ +From 46cab02a27010d74144a6342efc2b34961bb2e69 Mon Sep 17 00:00:00 2001 +From: Peter Jones +Date: Wed, 27 Sep 2017 11:28:00 -0400 +Subject: [PATCH 45/60] Fix incorrect test case and --remove-args with a +value. + +Currently we have this test case: + + grubTest grub.3 updargs/g3.4 --update-kernel=ALL +--remove-args="hdd=foobar" + +This fails to notice that the actual argument in grub.3 is hdd=ide-scsi, +and removes it anyway, and the data in g3.4 supports that behavior. +This is clearly wrong, and so this patch introduces updargs/g3.5, which +leaves hdd=ide-scsi intact, and fixes the code so that it won't modify +the command line in that case. + +Resolves: rhbz#1476273 + +Signed-off-by: Peter Jones +--- + grubby.c | 73 ++++++++++++++++++++++++++++++++++++++++------- + test.sh | 2 +- + test/results/updargs/g3.5 | 16 +++++++++++ + 3 files changed, 79 insertions(+), 12 deletions(-) + create mode 100644 test/results/updargs/g3.5 + +diff --git a/grubby.c b/grubby.c +index 951abb7..c2fab23 100644 +--- a/grubby.c ++++ b/grubby.c +@@ -3270,20 +3270,68 @@ static void removeElement(struct singleLine * line, int removeHere) { + line->numElements--; + } + +-int argMatch(const char * one, const char * two) { ++static int argNameMatch(const char *one, const char *two) ++{ + char * first, * second; +- char * chptr; ++ char *chptra, *chptrb; ++ int rc; + + first = strcpy(alloca(strlen(one) + 1), one); + second = strcpy(alloca(strlen(two) + 1), two); + +- chptr = strchr(first, '='); +- if (chptr) *chptr = '\0'; ++ chptra = strchr(first, '='); ++ if (chptra) ++ *chptra = '\0'; ++ ++ chptrb = strchr(second, '='); ++ if (chptrb) ++ *chptrb = '\0'; ++ ++ rc = strcmp(first, second); ++ ++ if (chptra) ++ *chptra = '='; ++ if (chptrb) ++ *chptrb = '='; ++ ++ return rc; ++} ++ ++static int argHasValue(const char *arg) ++{ ++ char *chptr; ++ ++ chptr = strchr(arg, '='); + +- chptr = strchr(second, '='); +- if (chptr) *chptr = '\0'; ++ if (chptr) ++ return 1; ++ return 0; ++} + +- return strcmp(first, second); ++static int argValueMatch(const char *one, const char *two) ++{ ++ char *first, *second; ++ char *chptra, *chptrb; ++ ++ first = strcpy(alloca(strlen(one) + 1), one); ++ second = strcpy(alloca(strlen(two) + 1), two); ++ ++ chptra = strchr(first, '='); ++ if (chptra) ++ chptra += 1; ++ ++ chptrb = strchr(second, '='); ++ if (chptrb) ++ chptrb += 1; ++ ++ if (!chptra && !chptrb) ++ return 0; ++ else if (!chptra) ++ return *chptrb - 0; ++ else if (!chptrb) ++ return 0 - *chptra; ++ else ++ return strcmp(chptra, chptrb); + } + + int updateActualImage(struct grubConfig * cfg, const char * image, +@@ -3419,7 +3467,7 @@ int updateActualImage(struct grubConfig * cfg, const char * image, + } + if (usedElements[i]) + continue; +- if (!argMatch(line->elements[i].item, *arg)) { ++ if (!argNameMatch(line->elements[i].item, *arg)) { + usedElements[i]=1; + break; + } +@@ -3470,9 +3518,12 @@ int updateActualImage(struct grubConfig * cfg, const char * image, + !strcmp(line->elements[i].item, "--")) + /* reached the end of hyper args, stop here */ + break; +- if (!argMatch(line->elements[i].item, *arg)) { +- removeElement(line, i); +- break; ++ if (!argNameMatch(line->elements[i].item, *arg)) { ++ if (!argHasValue(*arg) || ++ !argValueMatch(line->elements[i].item, *arg)) { ++ removeElement(line, i); ++ break; ++ } + } + } + /* handle removing LT_ROOT line too */ +diff --git a/test.sh b/test.sh +index a6da290..5d59216 100755 +--- a/test.sh ++++ b/test.sh +@@ -381,7 +381,7 @@ grubTest grub.3 updargs/g3.2 --update-kernel=DEFAULT \ + --args "root=/dev/hdd1 hdd=notide-scsi" + grubTest grub.3 updargs/g3.4 --update-kernel=ALL --remove-args="hdd" + grubTest grub.3 updargs/g3.4 --update-kernel=ALL --remove-args="hdd=ide-scsi" +-grubTest grub.3 updargs/g3.4 --update-kernel=ALL --remove-args="hdd=foobar" ++grubTest grub.3 updargs/g3.5 --update-kernel=ALL --remove-args="hdd=foobar" + grubTest grub.3 updargs/g3.7 --update-kernel=ALL \ + --remove-args="hdd root ro" + grubTest grub.7 updargs/g7.2 --boot-filesystem=/ \ +diff --git a/test/results/updargs/g3.5 b/test/results/updargs/g3.5 +new file mode 100644 +index 0000000..7d50bb8 +--- /dev/null ++++ b/test/results/updargs/g3.5 +@@ -0,0 +1,16 @@ ++#boot=/dev/hda ++timeout=10 ++splashimage=(hd0,1)/grub/splash.xpm.gz ++title Red Hat Linux (2.4.7-2smp) ++ root (hd0,1) ++ kernel /vmlinuz-2.4.7-2smp ro root=/dev/hda5 hdd=ide-scsi ++ initrd /initrd-2.4.7-2smp.img ++title Red Hat Linux-up (2.4.7-2) ++ root (hd0,1) ++ kernel /vmlinuz-2.4.7-2 ro root=/dev/hda5 hdd=ide-scsi ++ initrd /initrd-2.4.7-2.img ++title DOS ++ rootnoverify (hd0,0) ++ chainloader +1 ++ ++ +-- +2.19.1 + diff --git a/Fix-make-test-fail-for-g2-1.15.patch b/Fix-make-test-fail-for-g2-1.15.patch new file mode 100644 index 0000000..6b2ca10 --- /dev/null +++ b/Fix-make-test-fail-for-g2-1.15.patch @@ -0,0 +1,22 @@ +From c091abfca6c51079478431539b56df0e0d0346b1 Mon Sep 17 00:00:00 2001 +From: zhangguangzhi +Date: Wed, 18 Sep 2019 03:27:22 -0400 +Subject: [PATCH] fix test error g2-1.15 + + +diff --git a/test/results/add/g2-1.15 b/test/results/add/g2-1.15 +index b67c373..ee5f868 100644 +--- a/test/results/add/g2-1.15 ++++ b/test/results/add/g2-1.15 +@@ -82,7 +82,7 @@ menuentry 'Fedora 21 Rescue' --class fedora --class gnu-linux --class gnu --clas + search --no-floppy --fs-uuid --set=root 6169b46f-0257-4319-b2e4-caaed2a8e06b + fi + linuxefi /vmlinuz-0-rescue-5a94251776a14678911d4ae0949500f5 root=/fooooo ro rd.lvm.lv=fedora_uefi/root rd.lvm.lv=fedora_uefi/swap rhgb quiet +- initrdefi /initramfs-0-rescue-5a94251776a14678911d4ae0949500f5.img ++ initrd /initramfs-0-rescue-5a94251776a14678911d4ae0949500f5.img + } + menuentry 'Fedora, with Linux 3.15.0-0.rc5.git2.10.fc21.x86_64' --class fedora --class gnu-linux --class gnu --class os --unrestricted $menuentry_id_option 'gnulinux-3.15.0-0.rc5.git2.10.fc21.x86_64-advanced-a14e3dcb-ade3-42f7-832f-d9f66b5ae6a3' { + load_video +-- +2.19.1 + diff --git a/Print-default-image-even-if-isn-t-a-suitable-one.patch b/Print-default-image-even-if-isn-t-a-suitable-one.patch new file mode 100644 index 0000000..bbe0449 --- /dev/null +++ b/Print-default-image-even-if-isn-t-a-suitable-one.patch @@ -0,0 +1,51 @@ +From ee49b7b71d017097be5b4a0f32bff83379b0a86e Mon Sep 17 00:00:00 2001 +From: Javier Martinez Canillas +Date: Mon, 18 Mar 2019 12:53:23 +0100 +Subject: [PATCH 60/60] Print default image even if isn't a suitable one + +The grubby --default-kernel option only prints the default kernel if +this +is a suitable one. That is if its associated kernel cmdline root param +is +the same than the partition currently mounted as the filesystem root. + +But the grubby --set-default option doesn't have that restriction, it is +able to set a kernel as the default even if its root is for a different +partition. So make the --default-kernel option to also print the kernel +in this case. Still check if is a suitable image so --debug can tell it. + +Resolves: rhbz#1323842 + +Signed-off-by: Javier Martinez Canillas +--- + grubby.c | 4 +++- + test/results/debug/g2.1 | 1 + + 2 files changed, 4 insertions(+), 1 deletion(-) + +diff --git a/grubby.c b/grubby.c +index 522d4d5..c3249bf 100644 +--- a/grubby.c ++++ b/grubby.c +@@ -4887,7 +4887,9 @@ int main(int argc, const char ** argv) { + config->defaultImage = 0; + entry = findEntryByIndex(config, config->defaultImage); + if (!entry) return 0; +- if (!suitableImage(entry, bootPrefix, 0, flags)) return 0; ++ ++ /* check if is a suitable image but still print it */ ++ suitableImage(entry, bootPrefix, 0, flags); + + line = getLineByType(LT_KERNEL|LT_HYPER|LT_KERNEL_EFI|LT_KERNEL_16, entry->lines); + if (!line) return 0; +diff --git a/test/results/debug/g2.1 b/test/results/debug/g2.1 +index f5187f5..d579b59 100644 +--- a/test/results/debug/g2.1 ++++ b/test/results/debug/g2.1 +@@ -12,3 +12,4 @@ DBG: linux /vmlinuz-2.6.38.8-32.fc15.x86_64 root=/dev/mapper/vg_pjones5-lv_root + DBG: echo 'Loading initial ramdisk ...' + DBG: initrd /initramfs-2.6.38.8-32.fc15.x86_64.img + DBG: } ++/boot/vmlinuz-2.6.38.8-32.fc15.x86_64 +-- +2.19.1 + diff --git a/README.md b/README.md deleted file mode 100644 index f9cb355..0000000 --- a/README.md +++ /dev/null @@ -1,39 +0,0 @@ -# grubby - -#### 介绍 -{**以下是码云平台说明,您可以替换此简介** -码云是 OSCHINA 推出的基于 Git 的代码托管平台(同时支持 SVN)。专为开发者提供稳定、高效、安全的云端软件开发协作平台 -无论是个人、团队、或是企业,都能够用码云实现代码托管、项目管理、协作开发。企业项目请看 [https://gitee.com/enterprises](https://gitee.com/enterprises)} - -#### 软件架构 -软件架构说明 - - -#### 安装教程 - -1. xxxx -2. xxxx -3. xxxx - -#### 使用说明 - -1. xxxx -2. xxxx -3. xxxx - -#### 参与贡献 - -1. Fork 本仓库 -2. 新建 Feat_xxx 分支 -3. 提交代码 -4. 新建 Pull Request - - -#### 码云特技 - -1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md -2. 码云官方博客 [blog.gitee.com](https://blog.gitee.com) -3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解码云上的优秀开源项目 -4. [GVP](https://gitee.com/gvp) 全称是码云最有价值开源项目,是码云综合评定出的优秀开源项目 -5. 码云官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help) -6. 码云封面人物是一档用来展示码云会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) diff --git a/Set-envFile-from-env-when-bootloader-is-not-specifie.patch b/Set-envFile-from-env-when-bootloader-is-not-specifie.patch new file mode 100644 index 0000000..83a0570 --- /dev/null +++ b/Set-envFile-from-env-when-bootloader-is-not-specifie.patch @@ -0,0 +1,30 @@ +From 99727547fcab701f95e560f190e760540faef6f4 Mon Sep 17 00:00:00 2001 +From: "Brian C. Lane" +Date: Mon, 13 Apr 2015 13:57:33 -0700 +Subject: [PATCH 01/60] Set envFile from --env when bootloader is not specified + +--- + grubby.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/grubby.c b/grubby.c +index d4ebb86..53fe925 100644 +--- a/grubby.c ++++ b/grubby.c +@@ -4423,9 +4423,11 @@ int main(int argc, const char ** argv) { + } + + if (!cfi) { +- if (grub2FindConfig(&grub2ConfigType)) ++ if (grub2FindConfig(&grub2ConfigType)) { + cfi = &grub2ConfigType; +- else ++ if (envPath) ++ cfi->envFile = envPath; ++ } else + #ifdef __ia64__ + cfi = &eliloConfigType; + #elif __powerpc__ +-- +1.8.3.1 + diff --git a/drop-uboot-uImage-creation.patch b/drop-uboot-uImage-creation.patch new file mode 100644 index 0000000..ee611f9 --- /dev/null +++ b/drop-uboot-uImage-creation.patch @@ -0,0 +1,232 @@ +From 3689d4cebedf115e41c192bf034b6f86fcb80acb Mon Sep 17 00:00:00 2001 +From: Dennis Gilmore +Date: Wed, 30 Aug 2017 14:03:45 -0500 +Subject: [PATCH] remove the old crufty u-boot support + +Fedora has only supported extlinux.conf for a few releases now +as a result it should be the only way we boot systems. Remove +the no longer needed uboot file + +Signed-off-by: Dennis Gilmore +--- + new-kernel-pkg | 116 --------------------------------------------------------- + uboot | 43 --------------------- + 2 files changed, 159 deletions(-) + delete mode 100644 uboot + +diff --git a/new-kernel-pkg b/new-kernel-pkg +index 64225de..0fe6caa 100755 +--- a/new-kernel-pkg ++++ b/new-kernel-pkg +@@ -37,7 +37,6 @@ else + fi + + [ -f /etc/sysconfig/kernel ] && . /etc/sysconfig/kernel +-[ -f /etc/sysconfig/uboot ] && . /etc/sysconfig/uboot + + cfgGrub2="" + cfgGrub2Efi="" +@@ -50,7 +49,6 @@ grubConfig="" + grub2Config="" + grub2EfiConfig="" + extlinuxConfig="" +-ubootScript="/boot/boot.scr" + + ARCH=$(uname -m) + +@@ -84,13 +82,6 @@ elif [[ ${ARCH} =~ armv[5|7].*l ]] ; then + liloConfig="" + bootPrefix=/boot + extlinuxConfig=$(readlink -f /etc/extlinux.conf 2>/dev/null) +- ubootDir=${UBOOT_DIR:-"/boot"} +- ubootScript=$ubootDir/${UBOOT_SCR:-"boot.scr"} +- ubootKList=${UBOOT_KLIST:-"klist.txt"} +- ubootDevice=/dev/${UBOOT_DEVICE:-"mmcblk0p1"} +- ubootDefaultImage=${UBOOT_UIMAGE:-"uImage"} +- ubootDefaultInitrd=${UBOOT_UINITRD:-"uInitrd"} +- ubootAddress=${UBOOT_IMGADDR:-"0x00008000"} + mounted="" + liloFlag="" + isx86="" +@@ -386,53 +377,6 @@ remove() { + [ -n "$verbose" ] && echo "$liloConfig does not exist, not running grubby" + fi + +- if [ -n "$cfguBoot" ]; then +- [ -n "$verbose" ] && echo "removing $version from $ubootDir..." +- +- if [ -f $ubootDir/$ubootKList ]; then +- tmpKList=`mktemp $ubootDir/$ubootKList.XXXX` +- curversion=`tail -n1 $ubootDir/$ubootKList` +- sed "/$version$/d" $ubootDir/$ubootKList > $tmpKList +- newversion=`tail -n1 $tmpKList` +- if [ -f $ubootDir/uImage-$newversion ] && [ -f $ubootDir/uInitrd-$newversion ]; then +- if [ "$curversion" != "$newversion" ]; then +- cp -fp $ubootDir/uImage-$newversion $ubootDir/${ubootDefaultImage} +- if [ $? -ne 0 ]; then +- [ -n "$verbose" ] && echo "copy uImage-$newversion error, default kernel not replaced!" && exit +- fi +- cp -fp $ubootDir/uInitrd-$newversion $ubootDir/${ubootDefaultInitrd} +- if [ $? -ne 0 ]; then +- [ -n "$verbose" ] && echo "copy uInitrd-$newversion error, default Initrd not replaced!" && exit +- fi +- fi +- +- [ -n "$verbose" ] && echo "removing uImage-$version" +- if [ -f $ubootDir/uImage-$version ]; then +- rm -f $ubootDir/uImage-$version +- else +- [ -n "$verbose" ] && echo "uImage-$version did not exist!" +- fi +- +- [ -n "$verbose" ] && echo "removing uInitrd-$version" +- if [ -f $ubootDir/uInitrd-$version ]; then +- rm -f $ubootDir/uInitrd-$version +- else +- [ -n "$verbose" ] && echo "uInitrd-$version did not exist!" +- fi +- +- mv $tmpKList $ubootDir/$ubootKList +- [ -x /sbin/a-b-c ] && /sbin/a-b-c +- else +- [ -n "$verbose" ] && echo "uImage $newversion does not exist!" +- [ -f $tmpKList ] && rm -f $tmpKList +- fi +- else +- [ -n "$verbose" ] && echo "No previous kernel version. U-Boot images not removed!" +- fi +- else +- [ -n "$verbose" ] && echo "$ubootScript does not exist, not modifying $ubootDir" +- fi +- + if [ -n "$cfgExtlinux" ]; then + [ -n "$verbose" ] && echo "removing $version from $extlinuxConfig" + $grubby --extlinux -c $extlinuxConfig \ +@@ -534,36 +478,6 @@ update() { + [ -n "$verbose" ] && echo "$liloConfig does not exist, not running grubby" + fi + +- if [ -n "$cfguBoot" ]; then +- [ -n "$verbose" ] && echo "adding $version to $ubootDir..." +- +- [ -n "$verbose" ] && echo "creating uImage-$version" +- mkimage -A arm -O linux -T kernel -C none -a $ubootAddress \ +- -e $ubootAddress -n $version \ +- -d $kernelImage $ubootDir/uImage-$version +- +- [ -n "$verbose" ] && echo "creating uInitrd-$version" +- mkimage -A arm -O linux -T ramdisk -C none -a 0 -e 0 \ +- -n initramfs -d $initrdfile $ubootDir/uInitrd-$version +- +- if [ -f $ubootDir/uImage-$version ] && [ -f $ubootDir/uInitrd-$version ]; then +- cp -fp $ubootDir/uImage-$version $ubootDir/${ubootDefaultImage} +- if [ $? -ne 0 ]; then +- [ -n "$verbose" ] && echo "copy uImage-$version error, kernel not installed!" && exit +- fi +- cp -fp $ubootDir/uInitrd-$version $ubootDir/${ubootDefaultInitrd} +- if [ $? -ne 0 ]; then +- [ -n "$verbose" ] && echo "copy uInitrd-$version error, kernel not installed!" && exit +- fi +- echo $version >> $ubootDir/$ubootKList +- [ -x /sbin/a-b-c ] && /sbin/a-b-c +- else +- [ -n "$verbose" ] && echo "cannot make $version the default" +- fi +- else +- [ -n "$verbose" ] && echo "$ubootScript does not exist, not setting up $ubootDir" +- fi +- + if [ -n "$cfgExtlinux" ]; then + [ -n "$verbose" ] && echo "updating $version from $extlinuxConfig" + ARGS="--extlinux -c $extlinuxConfig --update-kernel=$kernelImage \ +@@ -874,33 +788,6 @@ fi + [ -n "$liloConfig" ] && [ -f "$liloConfig" ] && cfgLilo=1; + [ -n "$extlinuxConfig" ] && [ -f "$extlinuxConfig" ] && cfgExtlinux=1; + +-# if we have a U-Boot directory, but no boot script, check if the directory +-# is mounted. If not, mount it, and then check if a boot script exists. +-if [ -n "$ubootDir" ]; then +- if [ -f "$ubootScript" ]; then +- cfguBoot=1 +- else +- mountEntry=`mount | grep $ubootDir` +- if [ -z "$mountEntry" ]; then +- mount $ubootDevice $ubootDir +- mounted=1 +- fi +- [ -f "$ubootScript" ] && cfguBoot=1; +- fi +-fi +- +-# if we're using U-Boot, check if the default load address should change +-if [ -n "$cfguBoot" -a -z "$UBOOT_IMGADDR" ]; then +- [[ $version =~ .([^.]*)$ ]] +- platform=${BASH_REMATCH[1]} +- # A few platforms use an alternate kernel load address +- if [ "$platform" = "omap" ]; then +- ubootAddress=0x80008000 +- elif [ "$platform" = "imx" ]; then +- ubootAddress=0x90008000 +- fi +-fi +- + # if we have a lilo config on an x86 box, see if the default boot loader + # is lilo to determine if it should be run + if [ -n "$cfgLilo" -a -n "$isx86" ]; then +@@ -920,7 +807,4 @@ elif [ "$mode" == "--rpmposttrans" ]; then + rpmposttrans + fi + +-# if we mounted the U-Boot directory, unmount it. +-[ -n "$mounted" ] && umount $ubootDir +- + exit 0 +diff --git a/uboot b/uboot +deleted file mode 100644 +index 07d8671..0000000 +--- a/uboot ++++ /dev/null +@@ -1,43 +0,0 @@ +-# Settings for uBoot setup in /sbin/new-kernel-pkg +-# +-# Default values are provided below (as comments) +-# +-# WARNING: These values affect where grubby installs and removes +-# uBoot kernel images. Changing these _after_ kernels have +-# been installed may cause removing a kernel image to fail. +- +-# directory where uBoot images and scripts are found +-#UBOOT_DIR=/boot +- +-# Override the load address when running mkimage on the kernel. +-# OMAP such as Beagleboard and Pandaboard: Use 0x80008000 +-# Tegra such as Trimslice: Use 0x00008000 +-# IMX such as Efika mx51 smarttop: Use 0x90008000 +-# Kirkwood such as Dreamplug, Guruplug, Sheevaplug: Use 0x00008000 +-# If left undefined grubby will use defults for Tegra or OMAP depending +-# upon the contents of /proc/cpuinfo. +-#UBOOT_IMGADDR=0x0x00008000 +- +-# name of the text file containing the list of installed kernel versions +-# NOTE: The versions are in order of installation. The last entry should +-# always be the default boot kernel version. +-#UBOOT_KLIST=klist.txt +- +-# device partition where uBoot images reside; mounted on $UBOOT_DIR +-#UBOOT_DEVICE=mmcblk0p1 +- +- +-# NOTE: Both of the following files are automatically overwritte +-# when a kernel package is installed or removed. +- +-# default kernel uImage file name +-#UBOOT_UIMAGE=uImage +- +-# default initrd uInitrd file name +-#UBOOT_UINITRD=uInitrd +- +-# defualt for platform shipping an onboard dtb. +-#SHIPSDTB=no +- +-# option to tell new-kernel-pkg a specific dtb file to load in extlinux.conf +-#dtbfile=foo.dtb diff --git a/fix-make-test-fail-when-no-boot-partition.patch b/fix-make-test-fail-when-no-boot-partition.patch new file mode 100644 index 0000000..5f3ab32 --- /dev/null +++ b/fix-make-test-fail-when-no-boot-partition.patch @@ -0,0 +1,28 @@ +From 3cab2afc418f3363152708ab8bfaeb5380fa1385 Mon Sep 17 00:00:00 2001 +From: Luo Chunsheng +Date: Tue, 16 Apr 2019 05:02:46 -0400 +Subject: [PATCH] Currently we have this test case: grubDisplayTest grub.1 + defaultkernel/g.1 --default-kernel when no boot partition, it outputs without + "/boot" Prefix, then don't match defaultkernel/g.1, so add test option + "--boot-filesystem=/boot" to fix make test fail. + +--- + test.sh | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/test.sh b/test.sh +index 5d59216..6896985 100755 +--- a/test.sh ++++ b/test.sh +@@ -299,7 +299,7 @@ grubDisplayTest grub.10 defaulttitle/g.10 --default-title + grubDisplayTest grub.11 defaulttitle/g.11 --default-title + + testing="GRUB display default kernel" +-grubDisplayTest grub.1 defaultkernel/g.1 --default-kernel ++grubDisplayTest grub.1 defaultkernel/g.1 --boot-filesystem=/boot --default-kernel + + testing="LILO default directive" + liloTest lilo.1 default/l1.1 --set-default=/boot/vmlinuz-2.4.18-4 +-- +1.8.3.1 + diff --git a/grubby-Make-sure-configure-BOOTLOADER-variables-are-.patch b/grubby-Make-sure-configure-BOOTLOADER-variables-are-.patch new file mode 100644 index 0000000..96eac7f --- /dev/null +++ b/grubby-Make-sure-configure-BOOTLOADER-variables-are-.patch @@ -0,0 +1,60 @@ +From 171aaa84047acc3b1ed99cd7c1d8c7a5b0394b10 Mon Sep 17 00:00:00 2001 +From: Peter Jones +Date: Wed, 31 Jan 2018 13:06:48 -0500 +Subject: [PATCH 46/60] grubby: Make sure configure$BOOTLOADER variables +are + set correctly. + +When we've chosen a bootloader because it's default for a platform, and +we've already determined it's not overridden by the command line, set +the configure$BOOTLOADER variable to 1 so that our checks for which +bootloader are selected work correctly. + +Resolves: rhbz#1340893 + +Signed-off-by: Peter Jones +--- + grubby.c | 17 +++++++++++------ + 1 file changed, 11 insertions(+), 6 deletions(-) + +diff --git a/grubby.c b/grubby.c +index c2fab23..1d3e924 100644 +--- a/grubby.c ++++ b/grubby.c +@@ -4659,22 +4659,27 @@ int main(int argc, const char ** argv) { + if (!cfi) { + if (grub2FindConfig(&grub2ConfigType)) { + cfi = &grub2ConfigType; ++ configureGrub2 = 1; + if (envPath) + cfi->envFile = envPath; +- } else ++ } else { + #ifdef __ia64__ + cfi = &eliloConfigType; +- #elif __powerpc__ ++ configureLilo = 1; ++ #elif defined(__powerpc__) + cfi = &yabootConfigType; +- #elif __sparc__ ++ configureYaboot = 1; ++ #elif defined(__sparc__) + cfi = &siloConfigType; +- #elif __s390__ ++ configureSilo = 1; ++ #elif defined(__s390__) || defined(__s390x__) + cfi = &ziplConfigType; +- #elif __s390x__ +- cfi = &ziplConfigtype; ++ configureZipl = 1 + #else + cfi = &grubConfigType; ++ configureGrub = 1; + #endif ++ } + } + + if (!grubConfig) { +-- +2.19.1 + diff --git a/grubby-bls b/grubby-bls new file mode 100644 index 0000000..d94415f --- /dev/null +++ b/grubby-bls @@ -0,0 +1,616 @@ +#!/bin/bash +# +# grubby wrapper to manage BootLoaderSpec files +# +# +# Copyright 2018 Red Hat, Inc. All rights reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +readonly SCRIPTNAME="${0##*/}" + +CMDLINE_LINUX_DEBUG=" systemd.log_level=debug systemd.log_target=kmsg" +LINUX_DEBUG_VERSION_POSTFIX="_with_debugging" +LINUX_DEBUG_TITLE_POSTFIX=" with debugging" + +declare -a bls_file +declare -a bls_title +declare -a bls_version +declare -a bls_linux +declare -a bls_initrd +declare -a bls_options +declare -a bls_id + +[[ -f /etc/sysconfig/kernel ]] && . /etc/sysconfig/kernel +[[ -f /etc/os-release ]] && . /etc/os-release +read MACHINE_ID < /etc/machine-id +arch=$(uname -m) + +if [[ $arch = 's390' || $arch = 's390x' ]]; then + bootloader="zipl" +else + bootloader="grub2" +fi + +print_error() { + echo "$1" >&2 + exit 1 +} + +if [[ ${#} = 0 ]]; then + print_error "no action specified" +fi + +get_bls_value() { + local bls="$1" && shift + local key="$1" && shift + + echo "$(grep "^${key}[ \t]" "${bls}" | sed -e "s,^${key}[ \t]*,,")" +} + +set_bls_value() { + local bls="$1" && shift + local key="$1" && shift + local value="$1" && shift + + sed -i -e "s,^${key}.*,${key} ${value}," "${bls}" +} + +append_bls_value() { + local bls="$1" && shift + local key="$1" && shift + local value="$1" && shift + + old_value="$(get_bls_value ${bls} ${key})" + set_bls_value "${bls}" "${key}" "${old_value}${value}" +} + +get_bls_values() { + count=0 + for bls in $(ls -vr ${blsdir}/*.conf 2> /dev/null); do + bls_file[$count]="${bls}" + bls_title[$count]="$(get_bls_value ${bls} title)" + bls_version[$count]="$(get_bls_value ${bls} version)" + bls_linux[$count]="$(get_bls_value ${bls} linux)" + bls_initrd[$count]="$(get_bls_value ${bls} initrd)" + bls_options[$count]="$(get_bls_value ${bls} options)" + bls_id[$count]="$(get_bls_value ${bls} id)" + + count=$((count+1)) + done +} + +get_default_index() { + local default="" + local index="-1" + local title="" + local version="" + if [[ $bootloader = "grub2" ]]; then + default="$(grep '^saved_entry=' ${env} | sed -e 's/^saved_entry=//')" + else + default="$(grep '^default=' ${zipl_config} | sed -e 's/^default=//')" + fi + + if [[ -z $default ]]; then + index=0 + elif [[ $default =~ ^[0-9]+$ ]]; then + index="$default" + fi + + # GRUB2 and zipl use different fields to set the default entry + if [[ $bootloader = "grub2" ]]; then + title="$default" + else + version="$default" + fi + + for i in ${!bls_file[@]}; do + if [[ $title = ${bls_title[$i]} || $version = ${bls_version[$i]} || + $i -eq $index ]]; then + echo $i + return + fi + done +} + +display_default_value() { + case "$display_default" in + kernel) + echo "${bls_linux[$default_index]}" + exit 0 + ;; + index) + echo "$default_index" + exit 0 + ;; + title) + echo "${bls_title[$default_index]}" + exit 0 + ;; + esac +} + +param_to_indexes() { + local param="$1" + local indexes="" + + if [[ $param = "ALL" ]]; then + for i in ${!bls_file[@]}; do + indexes="$indexes $i" + done + echo -n $indexes + return + fi + + if [[ $param = "DEFAULT" ]]; then + echo -n $default_index + return + fi + + for i in ${!bls_file[@]}; do + if [[ $param = "${bls_linux[$i]}" ]]; then + indexes="$indexes $i" + fi + + if [[ $param = "TITLE=${bls_title[$i]}" ]]; then + indexes="$indexes $i" + fi + + if [[ $param = $i ]]; then + indexes="$indexes $i" + fi + done + + if [[ -n $indexes ]]; then + echo -n $indexes + return + fi + + echo -n "-1" +} + +display_info_values() { + local indexes=($(param_to_indexes "$1")) + + for i in ${indexes[*]}; do + echo "index=$i" + echo "kernel=${bls_linux[$i]}" + echo "args=\"${bls_options[$i]}\"" + echo "initrd=${bls_initrd[$i]}" + echo "title=${bls_title[$i]}" + done + exit 0 +} + +mkbls() { + local kernel=$1 && shift + local kernelver=$1 && shift + local datetime=$1 && shift + + local debugname="" + local flavor="" + + if [[ $kernelver == *\+* ]] ; then + local flavor=-"${kernelver##*+}" + if [[ $flavor == "-debug" ]]; then + local debugname="with debugging" + local debugid="-debug" + fi + fi + + cat < "${bls_target}" + fi + + if [[ -n $title ]]; then + set_bls_value "${bls_target}" "title" "${title}" + fi + + if [[ -n $options ]]; then + set_bls_value "${bls_target}" "options" "${options}" + fi + + if [[ -n $initrd ]]; then + set_bls_value "${bls_target}" "initrd" "${initrd}" + fi + + if [[ -n $extra_initrd ]]; then + append_bls_value "${bls_target}" "initrd" "${extra_initrd}" + fi + + if [[ $MAKEDEBUG = "yes" ]]; then + arch="$(uname -m)" + bls_debug="$(echo ${bls_target} | sed -e "s/\.${arch}/-debug.${arch}/")" + cp -aT "${bls_target}" "${bls_debug}" + append_bls_value "${bls_debug}" "title" "${LINUX_DEBUG_TITLE_POSTFIX}" + append_bls_value "${bls_debug}" "version" "${LINUX_DEBUG_VERSION_POSTFIX}" + append_bls_value "${bls_debug}" "options" "${CMDLINE_LINUX_DEBUG}" + blsid="$(get_bls_value ${bls_debug} "id" | sed -e "s/\.${arch}/-debug.${arch}/")" + set_bls_value "${bls_debug}" "id" "${blsid}" + fi + + get_bls_values + + if [[ $make_default = "true" ]]; then + set_default_bls "TITLE=${title}" + fi + + exit 0 +} + +update_args() { + local args=$1 && shift + local remove_args=($1) && shift + local add_args=($1) && shift + + for arg in ${remove_args[*]}; do + args="$(echo $args | sed -e "s,$arg[^ ]*,,")" + done + + for arg in ${add_args[*]}; do + if [[ $arg = *"="* ]]; then + value=${arg##*=} + key=${arg%%=$value} + exist=$(echo $args | grep "${key}=") + if [[ -n $exist ]]; then + args="$(echo $args | sed -e "s,$key=[^ ]*,$key=$value,")" + else + args="$args $key=$value" + fi + else + exist=$(echo $args | grep $arg) + if ! [[ -n $exist ]]; then + args="$args $arg" + fi + fi + done + + echo ${args} +} + +update_bls_fragment() { + local indexes=($(param_to_indexes "$1")) && shift + local remove_args=$1 && shift + local add_args=$1 && shift + local initrd=$1 && shift + + for i in ${indexes[*]}; do + if [[ -n $remove_args || -n $add_args ]]; then + local new_args="$(update_args "${bls_options[$i]}" "${remove_args}" "${add_args}")" + set_bls_value "${bls_file[$i]}" "options" "${new_args}" + fi + + if [[ -n $initrd ]]; then + set_bls_value "${bls_file[$i]}" "initrd" "${initrd}" + fi + done +} + +set_default_bls() { + local index=($(param_to_indexes "$1")) + + if [[ $bootloader = grub2 ]]; then + grub2-editenv "${env}" set saved_entry="${bls_title[$index]}" + else + local default="${bls_version[$index]}" + local current="$(grep '^default=' ${zipl_config} | sed -e 's/^default=//')" + if [[ -n $current ]]; then + sed -i -e "s,^default=.*,default=${default}," "${zipl_config}" + else + echo "default=${default}" >> "${zipl_config}" + fi + fi +} + +remove_var_prefix() { + if [[ -n $remove_kernel && $remove_kernel =~ ^/ ]]; then + remove_kernel="/${remove_kernel##*/}" + fi + + if [[ -n $initrd ]]; then + initrd="/${initrd##*/}" + fi + + if [[ -n $extra_initrd ]]; then + extra_initrd=" /${extra_initrd##*/}" + fi + + if [[ -n $kernel ]]; then + kernel="/${kernel##*/}" + fi + + if [[ -n $update_kernel && $update_kernel =~ ^/ ]]; then + update_kernel="/${update_kernel##*/}" + fi +} + +print_usage() +{ + cat <&2 + echo "Try '${SCRIPTNAME} --help' to list supported options" >&2 + echo + exit 1 + ;; + --) + shift + break + ;; + *) + echo + echo "${SCRIPTNAME}: invalid option \"${1}\"" >&2 + echo "Try '${SCRIPTNAME} --help' for more information" >&2 + echo + exit 1 + ;; + esac + shift +done + +if [[ -z $blsdir ]]; then + blsdir="/boot/loader/entries" +fi + +if [[ -z $env ]]; then + env="/boot/grub2/grubenv" +fi + +if [[ -z $zipl_config ]]; then + zipl_config="/etc/zipl.conf" +fi + +get_bls_values + +default_index="$(get_default_index)" + +if [[ -n $display_default ]]; then + display_default_value +fi + +if [[ -n $display_info ]]; then + display_info_values "${display_info}" +fi + +if [[ $bootloader = grub2 ]]; then + remove_var_prefix +fi + +if [[ -n $kernel ]]; then + if [[ $copy_default = "true" ]]; then + opts="${bls_options[$default_index]}" + if [[ -n $args ]]; then + opts="${opts} ${args}" + fi + else + opts="${args}" + fi + + add_bls_fragment "${kernel}" "${title}" "${opts}" "${initrd}" \ + "${extra_initrd}" +fi + +if [[ -n $remove_kernel ]]; then + remove_bls_fragment "${remove_kernel}" +fi + +if [[ -n $update_kernel ]]; then + update_bls_fragment "${update_kernel}" "${remove_args}" "${args}" "${initrd}" +fi + +if [[ -n $set_default ]]; then + set_default_bls "${set_default}" +fi + +exit 0 diff --git a/grubby-properly-handle-mixed-and-and-nested-quotes.patch b/grubby-properly-handle-mixed-and-and-nested-quotes.patch new file mode 100644 index 0000000..57df98c --- /dev/null +++ b/grubby-properly-handle-mixed-and-and-nested-quotes.patch @@ -0,0 +1,162 @@ +From 03433c27cbbeee9f8c83f5365b5ba1ef8c285d8b Mon Sep 17 00:00:00 2001 +From: Nishanth Aravamudan +Date: Tue, 16 Jun 2015 10:43:21 -0700 +Subject: [PATCH 04/60] grubby: properly handle mixed ' and " and nested quotes + +The SLES12 grub2.cfg file on ppc64le by default contains a line like: + + submenu "Bootable snapshot #$snapshot_num" { + menuentry "If OK, run 'snapper rollback $snapshot_num' reboot." { true; } + } + +On any grubby (tested with 8.40) invocation that updates the config +file, the combination of nested quotes and mixed quotes leads to a +generated file content like: + + submenu "Bootable snapshot #$snapshot_num" { + menuentry 'If OK, run snapper rollback $snapshot_num' rollback $snapshot_num' and reboot." { true; } + } + +which includes both a change from " to ', but also improperly quoted +strings and trailing characters relative to the string. This actually +leads to a failure to boot from the disk by default when using grubby +(e.g., Autotest) on SLES12 ppc64le. Whether SLES12 should be adding an +entry like this by default or not is probably open to debate, but grubby +should be able to hand this input file. + +To fix the issue, three changes were necessary: + +1) grub2ExtractTitle needs to check that if the second element starts +with a quote, that the matching element found ends with the same +quote-type (' vs. ") + +2) lineWrite needs to output the right kind of quote based upon if the +string to be outputted itself contains quotes. This is not currently +possible in the code, because quotes are stripped out normally by +readConfig, but with the change in 3), that only happens now for the +quotes that actually delineate a string. + +3) readConfig needs to check that when it is extracting titles and +determining extras, it uses matching quotes. + +With these changes, a simple grubby --set-default=SLES12 (for example), +now produces: + + submenu "Bootable snapshot #$snapshot_num" { + menuentry "If OK, run 'snapper rollback $snapshot_num' and reboot." { true; } + } + +as expected. + +Signed-off-by: Nishanth Aravamudan +--- + grubby.c | 42 +++++++++++++++++++++++++++++++++--------- + 1 file changed, 33 insertions(+), 9 deletions(-) + +diff --git a/grubby.c b/grubby.c +index 53fe925..440c627 100644 +--- a/grubby.c ++++ b/grubby.c +@@ -451,6 +451,8 @@ char *grub2ExtractTitle(struct singleLine * line) { + * whose last character is also quote (assuming it's the closing one) */ + int resultMaxSize; + char * result; ++ /* need to ensure that ' does not match " as we search */ ++ char quote_char = *current; + + resultMaxSize = sizeOfSingleLine(line); + result = malloc(resultMaxSize); +@@ -464,7 +466,7 @@ char *grub2ExtractTitle(struct singleLine * line) { + current_indent_len = strlen(current_indent); + + strncat(result, current_indent, current_indent_len); +- if (!isquote(current[current_len-1])) { ++ if (current[current_len-1] != quote_char) { + strncat(result, current, current_len); + } else { + strncat(result, current, current_len - 1); +@@ -928,10 +930,23 @@ static int lineWrite(FILE * out, struct singleLine * line, + /* Need to handle this, because we strip the quotes from + * menuentry when read it. */ + if (line->type == LT_MENUENTRY && i == 1) { +- if(!isquote(*line->elements[i].item)) +- fprintf(out, "\'%s\'", line->elements[i].item); +- else ++ if(!isquote(*line->elements[i].item)) { ++ int substring = 0; ++ /* If the line contains nested quotes, we did not strip ++ * the "interna" quotes and we must use the right quotes ++ * again when writing the updated file. */ ++ for (int j = i; j < line->numElements; j++) { ++ if (strchr(line->elements[i].item, '\'') != NULL) { ++ substring = 1; ++ fprintf(out, "\"%s\"", line->elements[i].item); ++ break; ++ } ++ } ++ if (!substring) ++ fprintf(out, "\'%s\'", line->elements[i].item); ++ } else { + fprintf(out, "%s", line->elements[i].item); ++ } + fprintf(out, "%s", line->elements[i].indent); + + continue; +@@ -1267,6 +1282,8 @@ static struct grubConfig * readConfig(const char * inName, + len = 0; + char *extras; + char *title; ++ /* initially unseen value */ ++ char quote_char = '\0'; + + for (int i = 1; i < line->numElements; i++) { + len += strlen(line->elements[i].item); +@@ -1283,13 +1300,16 @@ static struct grubConfig * readConfig(const char * inName, + for (int i = 0; i < line->numElements; i++) { + if (!strcmp(line->elements[i].item, "menuentry")) + continue; +- if (isquote(*line->elements[i].item)) ++ if (isquote(*line->elements[i].item) && quote_char == '\0') { ++ /* ensure we properly pair off quotes */ ++ quote_char = *line->elements[i].item; + title = line->elements[i].item + 1; +- else ++ } else { + title = line->elements[i].item; ++ } + + len = strlen(title); +- if (isquote(title[len-1])) { ++ if (title[len-1] == quote_char) { + strncat(buf, title,len-1); + break; + } else { +@@ -1300,6 +1320,7 @@ static struct grubConfig * readConfig(const char * inName, + + /* get extras */ + int count = 0; ++ quote_char = '\0'; + for (int i = 0; i < line->numElements; i++) { + if (count >= 2) { + strcat(extras, line->elements[i].item); +@@ -1310,12 +1331,15 @@ static struct grubConfig * readConfig(const char * inName, + continue; + + /* count ' or ", there should be two in menuentry line. */ +- if (isquote(*line->elements[i].item)) ++ if (isquote(*line->elements[i].item) && quote_char == '\0') { ++ /* ensure we properly pair off quotes */ ++ quote_char = *line->elements[i].item; + count++; ++ } + + len = strlen(line->elements[i].item); + +- if (isquote(line->elements[i].item[len -1])) ++ if (line->elements[i].item[len -1] == quote_char) + count++; + + /* ok, we get the final ' or ", others are extras. */ +-- +1.8.3.1 + diff --git a/grubby.in b/grubby.in new file mode 100644 index 0000000..f0036e9 --- /dev/null +++ b/grubby.in @@ -0,0 +1,8 @@ +#!/bin/bash +if [[ -x @@LIBEXECDIR@@/grubby-bls ]] ; then + exec @@LIBEXECDIR@@/grubby-bls "${@}" +elif [[ -x @@LIBEXECDIR@@/grubby ]] ; then + exec @@LIBEXECDIR@@/grubby "${@}" +fi +echo "Grubby is not installed correctly." >>/dev/stderr +exit 1 diff --git a/grubby.spec b/grubby.spec new file mode 100644 index 0000000..80c9449 --- /dev/null +++ b/grubby.spec @@ -0,0 +1,122 @@ +Name: grubby +Version: 8.40 +Release: 23 +Summary: Update and display information about the configuration files +License: GPLv2+ +URL: https://github.com/rhinstaller/grubby +Source0: https://github.com/rhboot/grubby/archive/%{version}-1.tar.gz +Source1: grubby-bls +Source2: grubby.in +Source3: installkernel.in +Patch1: drop-uboot-uImage-creation.patch +Patch2: 0001-Change-return-type-in-getRootSpecifier.patch +Patch3: 0002-Add-btrfs-subvolume-support-for-grub2.patch +Patch4: 0003-Use-system-LDFLAGS.patch +Patch5: 0004-Honor-sbindir.patch +Patch6: 0005-installkernel-use-kernel-install.patch + +Patch6001: Set-envFile-from-env-when-bootloader-is-not-specifie.patch +Patch6002: grubby-properly-handle-mixed-and-and-nested-quotes.patch +Patch6003: Drop-SEGV-handler.patch +Patch6004: Add-a-bunch-of-tests-for-various-default-kernel-titl.patch +Patch6005: Emit-better-systemd-debug-settings-on-debug-entries.patch +Patch6006: Don-t-leak-from-one-extractTitle-call.patch +Patch6007: Fix-dracut-cmdline-options-and-conditionalize-them-t.patch +Patch6008: Always-do-the-rungrubby-debug-after-the-normal-kerne.patch +Patch6009: Be-more-thorough-about-flushing-our-config-file-when.patch +Patch6010: Fix-incorrect-test-case-and-remove-args-with-a-value.patch +Patch6011: grubby-Make-sure-configure-BOOTLOADER-variables-are-.patch +Patch6012: Fix-GCC-warnings-about-possible-string-truncations-a.patch +Patch6013: Check-that-pointers-are-not-NULL-before-dereferencin.patch +Patch6014: Print-default-image-even-if-isn-t-a-suitable-one.patch + +Patch9001: fix-make-test-fail-when-no-boot-partition.patch +Patch9002: Fix-make-test-fail-for-g2-1.15.patch + +BuildRequires: gcc pkgconfig glib2-devel popt-devel +BuildRequires: libblkid-devel git-core sed make +BuildRequires: util-linux-ng +%ifarch aarch64 i686 x86_64 +BuildRequires: grub2-tools-minimal +Requires: grub2-tools +%endif + +%description +grubby is a command line tool for updating and displaying information about +the configuration files for the grub, lilo, elilo (ia64), yaboot (powerpc) +and zipl (s390) boot loaders. It is primarily designed to be used from scripts +which install new kernels and need to find information about the current boot +environment. + +%package bls +Summary: a command line tool for updating bootloader configs +Conflicts: %{name} <= 8.40-13 +BuildArch: noarch + +%description bls +the package provides a grubby wrapper that manages BootLoaderSpec files and is +meant to only be used for legacy compatibility users with existing grubby users. + +%package_help + +%prep +%autosetup -n %{name}-%{version}-1 -p1 + +%build +%make_build + +%check +make test + +%install +%make_install mandir=%{_mandir} sbindir=%{_sbindir} + +mkdir -p %{buildroot}%{_libexecdir}/{grubby,installkernel}/ %{buildroot}%{_sbindir}/ +mv -v %{buildroot}%{_sbindir}/grubby %{buildroot}%{_libexecdir}/grubby/grubby +mv -v %{buildroot}%{_sbindir}/installkernel %{buildroot}%{_libexecdir}/installkernel/installkernel +cp -v %{SOURCE1} %{buildroot}%{_libexecdir}/grubby/ +sed -e "s,@@LIBEXECDIR@@,%{_libexecdir}/grubby,g" %{SOURCE2} > %{buildroot}%{_sbindir}/grubby +sed -e "s,@@LIBEXECDIR@@,%{_libexecdir}/installkernel,g" %{SOURCE3} > %{buildroot}%{_sbindir}/installkernel + +%pre + +%preun + +%post + +%postun + +%files +%license COPYING +%dir %{_libexecdir}/grubby +%dir %{_libexecdir}/installkernel +%attr(0755,root,root) %{_libexecdir}/grubby/grubby +%attr(0755,root,root) %{_libexecdir}/installkernel/installkernel +%attr(0755,root,root) %{_sbindir}/grubby +%attr(0755,root,root) %{_sbindir}/installkernel +%attr(0755,root,root) %{_sbindir}/new-kernel-pkg + +%files bls +%dir %{_libexecdir}/grubby +%attr(0755,root,root) %{_libexecdir}/grubby/grubby-bls +%attr(0755,root,root) %{_sbindir}/grubby + +%files help +%defattr(-,root,root) +%{_mandir}/man8/*.8* + +%changelog +* Mon Dec 30 2019 openEuler Buildteam - 8.40-23 +- Modify patch info + +* Sat Nov 30 2019 openEuler Buildteam - 8.40-22 +- add package bls to fix kernel package installation error + +* Thu Sep 26 2019 openEuler Buildteam - 8.40-21 +- Modify patch info + +* Mon Sep 23 2019 openEuler Buildteam - 8.40-20 +- Modify Requires + +* Wed Sep 18 2019 openEuler Buildteam - 8.40-19 +- Package init diff --git a/installkernel.in b/installkernel.in new file mode 100644 index 0000000..87b81ee --- /dev/null +++ b/installkernel.in @@ -0,0 +1,8 @@ +#!/bin/bash +if [[ -x @@LIBEXECDIR@@/installkernel ]] ; then + exec @@LIBEXECDIR@@/installkernel "${@}" +elif [[ -x @@LIBEXECDIR@@/installkernel-bls ]] ; then + exec @@LIBEXECDIR@@/installkernel-bls "${@}" +fi +echo "installkernel is not installed correctly." >>/dev/stderr +exit 1