From 4a8bdeb3c9abb8180626fdc2e0822dde07f82ac7 Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Mon, 2 Jul 2007 15:14:02 +0200 Subject: applied add-grub-mbr-default3.diff This version ignores GRUB version. --- util/Makefile.am | 4 + util/Makefile.in | 78 ++++++++++++++++------ util/grub-mbr-default.c | 171 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 233 insertions(+), 20 deletions(-) diff --git a/util/Makefile.am b/util/Makefile.am index 2e04711..127e857 100644 --- a/util/Makefile.am +++ b/util/Makefile.am @@ -1,4 +1,5 @@ bin_PROGRAMS = mbchk +sbin_PROGRAMS = grub-mbr-default sbin_SCRIPTS = grub-install grub-md5-crypt grub-terminfo \ grub-set-default noinst_SCRIPTS = grub-image mkbimage @@ -7,6 +8,9 @@ EXTRA_DIST = mkbimage # XXX: Need to search for a header file in docs, because of multiboot.h. AM_CFLAGS = -I$(top_srcdir)/lib -I$(top_srcdir)/docs +INCLUDES = -I$(top_srcdir)/stage2 -I$(top_srcdir)/stage1 mbchk_SOURCES = mbchk.c mbchk_LDADD = ../lib/libcommon.a + +grub_mbr_default_SOURCES = grub-mbr-default.c diff --git a/util/Makefile.in b/util/Makefile.in index 6beedf7..6fba6ef 100644 --- a/util/Makefile.in +++ b/util/Makefile.in @@ -15,7 +15,7 @@ @SET_MAKE@ -SOURCES = $(mbchk_SOURCES) +SOURCES = $(grub_mbr_default_SOURCES) $(mbchk_SOURCES) srcdir = @srcdir@ top_srcdir = @top_srcdir@ @@ -39,6 +39,7 @@ PRE_UNINSTALL = : POST_UNINSTALL = : host_triplet = @host@ bin_PROGRAMS = mbchk$(EXEEXT) +sbin_PROGRAMS = grub-mbr-default$(EXEEXT) subdir = util DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in \ $(srcdir)/grub-image.in $(srcdir)/grub-install.in \ @@ -53,9 +54,13 @@ mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = grub-image grub-install grub-md5-crypt \ grub-terminfo grub-set-default -am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(sbindir)" +am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(sbindir)" binPROGRAMS_INSTALL = $(INSTALL_PROGRAM) -PROGRAMS = $(bin_PROGRAMS) +sbinPROGRAMS_INSTALL = $(INSTALL_PROGRAM) +PROGRAMS = $(bin_PROGRAMS) $(sbin_PROGRAMS) +am_grub_mbr_default_OBJECTS = grub-mbr-default.$(OBJEXT) +grub_mbr_default_OBJECTS = $(am_grub_mbr_default_OBJECTS) +grub_mbr_default_LDADD = $(LDADD) am_mbchk_OBJECTS = mbchk.$(OBJEXT) mbchk_OBJECTS = $(am_mbchk_OBJECTS) mbchk_DEPENDENCIES = ../lib/libcommon.a @@ -64,13 +69,14 @@ SCRIPTS = $(noinst_SCRIPTS) $(sbin_SCRIPTS) DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles -@AMDEP_TRUE@DEP_FILES = ./$(DEPDIR)/mbchk.Po +@AMDEP_TRUE@DEP_FILES = ./$(DEPDIR)/grub-mbr-default.Po \ +@AMDEP_TRUE@ ./$(DEPDIR)/mbchk.Po COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) CCLD = $(CC) LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ -SOURCES = $(mbchk_SOURCES) -DIST_SOURCES = $(mbchk_SOURCES) +SOURCES = $(grub_mbr_default_SOURCES) $(mbchk_SOURCES) +DIST_SOURCES = $(grub_mbr_default_SOURCES) $(mbchk_SOURCES) ETAGS = etags CTAGS = ctags DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) @@ -189,8 +195,10 @@ EXTRA_DIST = mkbimage # XXX: Need to search for a header file in docs, because of multiboot.h. AM_CFLAGS = -I$(top_srcdir)/lib -I$(top_srcdir)/docs +INCLUDES = -I$(top_srcdir)/stage2 -I$(top_srcdir)/stage1 mbchk_SOURCES = mbchk.c mbchk_LDADD = ../lib/libcommon.a +grub_mbr_default_SOURCES = grub-mbr-default.c all: all-am .SUFFIXES: @@ -257,6 +265,32 @@ uninstall-binPROGRAMS: clean-binPROGRAMS: -test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS) +install-sbinPROGRAMS: $(sbin_PROGRAMS) + @$(NORMAL_INSTALL) + test -z "$(sbindir)" || $(mkdir_p) "$(DESTDIR)$(sbindir)" + @list='$(sbin_PROGRAMS)'; for p in $$list; do \ + p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \ + if test -f $$p \ + ; then \ + f=`echo "$$p1" | sed 's,^.*/,,;$(transform);s/$$/$(EXEEXT)/'`; \ + echo " $(INSTALL_PROGRAM_ENV) $(sbinPROGRAMS_INSTALL) '$$p' '$(DESTDIR)$(sbindir)/$$f'"; \ + $(INSTALL_PROGRAM_ENV) $(sbinPROGRAMS_INSTALL) "$$p" "$(DESTDIR)$(sbindir)/$$f" || exit 1; \ + else :; fi; \ + done + +uninstall-sbinPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(sbin_PROGRAMS)'; for p in $$list; do \ + f=`echo "$$p" | sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \ + echo " rm -f '$(DESTDIR)$(sbindir)/$$f'"; \ + rm -f "$(DESTDIR)$(sbindir)/$$f"; \ + done + +clean-sbinPROGRAMS: + -test -z "$(sbin_PROGRAMS)" || rm -f $(sbin_PROGRAMS) +grub-mbr-default$(EXEEXT): $(grub_mbr_default_OBJECTS) $(grub_mbr_default_DEPENDENCIES) + @rm -f grub-mbr-default$(EXEEXT) + $(LINK) $(grub_mbr_default_LDFLAGS) $(grub_mbr_default_OBJECTS) $(grub_mbr_default_LDADD) $(LIBS) mbchk$(EXEEXT): $(mbchk_OBJECTS) $(mbchk_DEPENDENCIES) @rm -f mbchk$(EXEEXT) $(LINK) $(mbchk_LDFLAGS) $(mbchk_OBJECTS) $(mbchk_LDADD) $(LIBS) @@ -286,6 +320,7 @@ mostlyclean-compile: distclean-compile: -rm -f *.tab.c +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/grub-mbr-default.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mbchk.Po@am__quote@ .c.o: @@ -384,7 +419,7 @@ check-am: all-am check: check-am all-am: Makefile $(PROGRAMS) $(SCRIPTS) installdirs: - for dir in "$(DESTDIR)$(bindir)" "$(DESTDIR)$(sbindir)"; do \ + for dir in "$(DESTDIR)$(bindir)" "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(sbindir)"; do \ test -z "$$dir" || $(mkdir_p) "$$dir"; \ done install: install-am @@ -413,7 +448,8 @@ maintainer-clean-generic: @echo "it deletes files that may require special tools to rebuild." clean: clean-am -clean-am: clean-binPROGRAMS clean-generic mostlyclean-am +clean-am: clean-binPROGRAMS clean-generic clean-sbinPROGRAMS \ + mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) @@ -433,7 +469,8 @@ info-am: install-data-am: -install-exec-am: install-binPROGRAMS install-sbinSCRIPTS +install-exec-am: install-binPROGRAMS install-sbinPROGRAMS \ + install-sbinSCRIPTS install-info: install-info-am @@ -459,19 +496,20 @@ ps: ps-am ps-am: uninstall-am: uninstall-binPROGRAMS uninstall-info-am \ - uninstall-sbinSCRIPTS + uninstall-sbinPROGRAMS uninstall-sbinSCRIPTS .PHONY: CTAGS GTAGS all all-am check check-am clean clean-binPROGRAMS \ - clean-generic ctags distclean distclean-compile \ - distclean-generic distclean-tags distdir dvi dvi-am html \ - html-am info info-am install install-am install-binPROGRAMS \ - install-data install-data-am install-exec install-exec-am \ - install-info install-info-am install-man install-sbinSCRIPTS \ - install-strip installcheck installcheck-am installdirs \ - maintainer-clean maintainer-clean-generic mostlyclean \ - mostlyclean-compile mostlyclean-generic pdf pdf-am ps ps-am \ - tags uninstall uninstall-am uninstall-binPROGRAMS \ - uninstall-info-am uninstall-sbinSCRIPTS + clean-generic clean-sbinPROGRAMS ctags distclean \ + distclean-compile distclean-generic distclean-tags distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-binPROGRAMS install-data install-data-am install-exec \ + install-exec-am install-info install-info-am install-man \ + install-sbinPROGRAMS install-sbinSCRIPTS install-strip \ + installcheck installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic pdf pdf-am ps ps-am tags uninstall \ + uninstall-am uninstall-binPROGRAMS uninstall-info-am \ + uninstall-sbinPROGRAMS uninstall-sbinSCRIPTS # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. diff --git a/util/grub-mbr-default.c b/util/grub-mbr-default.c new file mode 100644 index 0000000..6664b77 --- /dev/null +++ b/util/grub-mbr-default.c @@ -0,0 +1,171 @@ +/* + * grub-mbr-default - a boot entry validation tool for the GRUB bootloader. + * + * Copyright (C) 2005,2007 Willy Tarreau + * + * 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* from stage2/shared.h */ +#define STAGE2_VER_MAJ_OFFS 0x6 +#define STAGE2_INSTALLPART 0x8 +#define STAGE2_SAVED_ENTRYNO 0xc +#define STAGE2_STAGE2_ID 0x10 +#define STAGE2_FORCE_LBA 0x11 +#define STAGE2_VER_STR_OFFS 0x12 +#define SECTOR_SIZE 0x200 + + +void usage(char *name); +void error(int err, char *msg); +void die(int err, char *msg); + +void error(int err, char *msg) { + perror(msg); + exit(err); +} + +void die(int err, char *msg) { + fprintf(stderr, "%s\n", msg); + exit(err); +} + +void usage(char *name) { + char *n; + n = strrchr(name, '/'); + if (n) + name = n + 1; + + fprintf(stderr, + "%s: validate last boot entry in GRUB MBR.\n" + "Usage:\n" + "\t%s [-q] \n" + "\t\tshows what next boot entry will be used on device .\n" + "\t\tThe -q argument makes the program quiet.\n" + "\n\t%s [-q] \n" + "\t\tsets next boot entry to on device .\n" + "\n\t%s [-q] <-round>\n" + "\t\tsets next boot entry to (current-1) rounded to .\n" + "\t\tThis permits to revalidate the first entry in a series of\n" + "\t\t retries leading to a successful boot.\n" + "", name, name, name, name); + + exit(1); +} + +int main(int argc, char **argv) { + char sector[SECTOR_SIZE]; + char *device, *progname; + int fd, readonly, quiet, stop; + int entryno, new_entryno; + + progname = argv[0]; + + argc--; argv++; + quiet = stop = new_entryno = 0; + while (argc && !stop && **argv == '-') { + switch (argv[0][1]) { + case 'q': + quiet = 1; + break; + case '-': + stop = 1; + break; + default: + usage(progname); + } + argc--; argv++; + } + + if (argc < 1) + usage(progname); + + device = argv[0]; + readonly = (argc < 2); + if (!readonly) + new_entryno = atoi(argv[1]); + + fd = open(device, readonly ? O_RDONLY : O_RDWR); + if (fd < 0) + error(1, "Failed to open the device :"); + + if (lseek(fd, SECTOR_SIZE * 2, SEEK_SET) < 0) + error(2, "Cannot seek to the sector :"); + + if (read(fd, sector, SECTOR_SIZE) != SECTOR_SIZE) + error(2, "Read error or partial read :"); + + /* sanity checks */ + if (sector[STAGE2_VER_MAJ_OFFS] != COMPAT_VERSION_MAJOR || + sector[STAGE2_VER_MAJ_OFFS+1] != COMPAT_VERSION_MINOR || + *(unsigned *)§or[STAGE2_INSTALLPART] != 0xFFFFFF) { + die(3, "Aborting: GRUB MBR signature not found."); + } + + if (!quiet) + printf("Found GRUB %c%c%c%c MBR signature.\n", + sector[STAGE2_VER_STR_OFFS], + sector[STAGE2_VER_STR_OFFS+1], + sector[STAGE2_VER_STR_OFFS+2], + sector[STAGE2_VER_STR_OFFS+3] + ); + + entryno = *(unsigned *)§or[STAGE2_SAVED_ENTRYNO]; + if (readonly) { + if (!quiet) + printf("Next boot will use entry #"); + printf("%d\n", entryno); + exit(0); + } + + /* This needs some explanation: the 'savedefault fallback' config statement + * saves the next entry number before booting the current one. So assuming + * the configuration uses consecutive fallbacks, the current entry number + * equals the saved entry number minus 1. This mode allows one to dedicate + * several entries to a number of retries, so that if entry 2 fails because + * of power failure, a strictly equivalent entry 3 gives it a second chance + * to boot. If entry 3 succeeds and if it is a backup of entry 2, we must + * validate 2 and not 3. This is what is provided here when new_entryno is + * negative. It reflects the opposite of the number of consecutive equivalent + * entries in the configuration file. For instance, if each entry is doubled, + * then new_entryno should be -2. + */ + + if (new_entryno < 0) + new_entryno = (entryno - 1) - (entryno - 1) % (-new_entryno); + + *(unsigned *)§or[STAGE2_SAVED_ENTRYNO] = new_entryno; + + if (lseek(fd, SECTOR_SIZE * 2, SEEK_SET) < 0) + error(4, "Cannot seek to the sector :"); + + if (write(fd, sector, SECTOR_SIZE) != SECTOR_SIZE) + error(4, "Write error :"); + + if (!quiet) + printf("Next boot entry changed to #%d\n", new_entryno); + close(fd); + exit(0); +} -- 1.4.4.3