diff --git a/grub2-once b/grub2-once index 3de03da..464d003 100644 --- a/grub2-once +++ b/grub2-once @@ -15,12 +15,12 @@ my @menuentry; sub parse_menuentry { my ($parent, $menu) = @_; - my @m = $menu =~ /(submenu|menuentry) \s+ '([^']*)' .*? ( \{ (?: [^{}]* | (?3))* \} )/sxg; + my @m = $menu =~ /(submenu|menuentry) \s+ (.*?) ( \{ (?: [^{}]* | (?3))* \} )/sxg; for (my $i = 0; $i <= $#m; $i += 3) { my $type = $m[$i]; - my $title = $m[$i+1]; + my $title = `printf "%s\n" $m[$i+1] | head -1 | tr -d '\n'`; my $data = $m[$i+2]; my $name = ($parent) ? "$parent>$title" : "$title"; diff --git a/grub2-systemd-sleep.sh b/grub2-systemd-sleep.sh new file mode 100644 index 0000000..06a76a3 --- /dev/null +++ b/grub2-systemd-sleep.sh @@ -0,0 +1,211 @@ +#!/bin/bash +# +# Copyright (c) 2015 SUSE LINUX Products GmbH, Nuernberg, Germany. + +set -e + +GRUB_ONCE="/usr/sbin/grub2-once" +GRUB_ENV="/boot/grub2/grubenv" +GRUB_EDITENV="/usr/bin/grub2-editenv" +GRUB_CONF="/boot/grub2/grub.cfg" +BLKID="/usr/sbin/blkid" + +error_quit() +{ + echo "$1" >&2 + exit 1 +} + +check-system() +{ + [ -x "${GRUB_ONCE}" ] || error_quit "ERROR: cannot find or execute ${GRUB_EDITENV}" + [ -x "${GRUB_EDITENV}" ] || error_quit "ERROR: cannot find or execute ${GRUB_EDITENV}" + [ -x "${BLKID}" ] || error_quit "ERROR: cannot find or execute ${BLKID}" + [ -r "${GRUB_CONF}" ] || error_quit "ERROR: cannot find or read ${GRUB_CONF}" +} + +##################################################################### +# gets a list of available kernels from /boot/grub2/grub.cfg +# kernels are in the array $KERNELS +get-kernels() +{ + local I DUMMY MNT ROOTDEV + declare -i I=0 + + # we need the root partition later to decide if this is the kernel to select + while read ROOTDEV MNT DUMMY; do + [ "$ROOTDEV" = "rootfs" ] && continue # not what we are searching for + if [ "$MNT" = "/" ]; then + break + fi + done < /proc/mounts + + + while read -r LINE; do + case $LINE in + menuentry\ *) + local PATTERN="^\\s*menuentry\\s\\+\\(.\\+\\)\\s*{.*\$" + MENUENTRY_OPTS=`echo "$LINE" | sed -n -e "s/${PATTERN}/\\1/p"` + MENU_ENTRIES[$I]=`eval "printf \"%s\n\" $MENUENTRY_OPTS | head -1"` + ;; + set\ default*) + local DEFAULT=${LINE#*default=} + + if echo $DEFAULT | grep -q saved_entry ; then + local SAVED=`$GRUB_EDITENV $GRUB_ENV list | sed -n s/^saved_entry=//p` + if [ -n "$SAVED" ]; then + DEFAULT_BOOT=$($GRUB_ONCE --show-mapped "$SAVED") + fi + fi + + ;; + linux*noresume*|module*xen*noresume*) + echo " Skipping grub entry #${J}, because it has the noresume option" >&2 + ;; + linux*root=*|module*xen*root=*) + local ROOT + ROOT=${LINE#*root=} + DUMMY=($ROOT) + ROOT=${DUMMY[0]} + + if [ x"${ROOT:0:5}" = "xUUID=" ]; then + UUID=${ROOT#UUID=} + if [ -n "$UUID" ]; then + ROOT=$($BLKID -U $UUID) + fi + fi + + if [ "$(stat -Lc '%t:%T' $ROOT)" != "$(stat -Lc '%t:%T' $ROOTDEV)" ]; then + echo " Skipping grub entry #${J}, because its root= parameter ($ROOT)" >&2 + echo " does not match the current root device ($ROOTDEV)." >&2 + continue + fi + DUMMY=($LINE) # kernel (hd0,1)/boot/vmlinuz-ABC root=/dev/hda2 + KERNELS[$I]=${DUMMY[1]##*/} # vmlinuz-ABC + # DEBUG "Found kernel entry #${I}: '${DUMMY[1]##*/}'" INFO + let ++I + ;; + linux*|module*xen*) + # a kernel without "root="? We better skip that one... + echo " Skipping grub entry #${J}, because it has no root= option" >&2 + ;; + *) ;; + esac + done < "$GRUB_CONF" +} + +############################################################# +# restore grub default after (eventually failed) resume +grub-once-restore() +{ + echo "INFO: Running grub-once-restore .." + check-system + $GRUB_EDITENV $GRUB_ENV unset next_entry + echo "INFO: Done." +} + +############################################################################# +# try to find a kernel image that matches the actually running kernel. +# We need this, if more than one kernel is installed. This works reasonably +# well with grub, if all kernels are named "vmlinuz-`uname -r`" and are +# located in /boot. If they are not, good luck ;-) +find-kernel-entry() +{ + NEXT_BOOT="" + ARCH=`uname -m` + declare -i I=0 + # DEBUG "running kernel: $RUNNING" DIAG + while [ -n "${KERNELS[$I]}" ]; do + BOOTING="${KERNELS[$I]}" + if IMAGE=`readlink /boot/$BOOTING` && [ -e "/boot/${IMAGE##*/}" ]; then + # DEBUG "Found kernel symlink $BOOTING => $IMAGE" INFO + BOOTING=$IMAGE + fi + case $ARCH in + ppc*) BOOTING="${BOOTING#*vmlinux-}" ;; + *) BOOTING="${BOOTING#*vmlinuz-}" ;; + esac + if [ "$RUNNING" == "$BOOTING" -a -n "${MENU_ENTRIES[$I]}" ]; then + NEXT_BOOT="${MENU_ENTRIES[$I]}" + echo " running kernel is grub menu entry $NEXT_BOOT (${KERNELS[$I]})" + break + fi + let ++I + done + # if we have not found a kernel, issue a warning. + # if we have found a kernel, we'll do "grub-once" later, after + # prepare_suspend finished. + if [ -z "$NEXT_BOOT" ]; then + echo "WARNING: no kernelfile matching the running kernel found" + fi +} + +############################################################################# +# if we did not find a kernel (or BOOT_LOADER is not GRUB) check, +# if the running kernel is still the one that will (probably) be booted for +# resume (default entry in menu.lst or, if there is none, the kernel file +# /boot/vmlinuz points to.) +# This will only work, if you use "original" SUSE kernels. +# you can always override with the config variable set to "yes" +prepare-grub() +{ + echo "INFO: Running prepare-grub .." + check-system + get-kernels + RUNNING=`uname -r` + find-kernel-entry + + if [ -z "$NEXT_BOOT" ]; then + # which kernel is booted with the default entry? + BOOTING="${KERNELS[$DEFAULT_BOOT]}" + # if there is no default entry (no menu.lst?) we fall back to + # the default of /boot/vmlinuz. + [ -z "$BOOTING" ] && BOOTING="vmlinuz" + if IMAGE=`readlink /boot/$BOOTING` && [ -e "/boot/${IMAGE##*/}" ]; then + BOOTING=$IMAGE + fi + BOOTING="${BOOTING#*vmlinuz-}" + echo "running kernel: '$RUNNING', probably booting kernel: '$BOOTING'" + if [ "$BOOTING" != "$RUNNING" ]; then + error_quit "ERROR: kernel version mismatch, cannot suspend to disk" + fi + else + # set the bootloader to the running kernel + echo " preparing boot-loader: selecting entry $NEXT_BOOT, kernel /boot/$BOOTING" + T1=`date +"%s%N"` + sync; sync; sync # this is needed to speed up grub-once on reiserfs + T2=`date +"%s%N"` + echo " running $GRUB_ONCE \"${NEXT_BOOT}\"" + ${GRUB_ONCE} "$NEXT_BOOT" + T3=`date +"%s%N"` + S=$(((T2-T1)/100000000)); S="$((S/10)).${S:0-1}" + G=$(((T3-T2)/100000000)); G="$((G/10)).${G:0-1}" + echo " time needed for sync: $S seconds, time needed for grub: $G seconds." + fi + + echo "INFO: Done." +} + + +###### main() + +eval `grep LOADER_TYPE= /etc/sysconfig/bootloader` + +if [ x"$LOADER_TYPE" != "xgrub2" -a x"$LOADER_TYPE" != "xgrub2-efi" ]; then + echo "INFO: Skip running $0 for bootloader: $LOADER_TYPE" + exit 0 +fi + +if [ "$2" = suspend ]; then + echo "INFO: Skip running $0 for $2" + exit 0 +else + echo "INFO: running $0 for $2" +fi + +if [ "$1" = pre ] ; then + prepare-grub +fi +if [ "$1" = post ] ; then + grub-once-restore +fi diff --git a/grub2.changes b/grub2.changes index 1870522..47c0e4d 100644 --- a/grub2.changes +++ b/grub2.changes @@ -1,3 +1,11 @@ +------------------------------------------------------------------- +Tue Aug 18 09:53:54 UTC 2015 - mchang@suse.com + +- add systemd-sleep-plugin subpackage (bsc#941758) +- evaluate the menu entry's title string by printf + * modified grub2-once + * added grub2-systemd-sleep.sh + ------------------------------------------------------------------- Fri Jul 31 03:55:32 UTC 2015 - mchang@suse.com diff --git a/grub2.spec b/grub2.spec index 10506f9..96fb7d0 100644 --- a/grub2.spec +++ b/grub2.spec @@ -1,7 +1,7 @@ # # spec file for package grub2 # -# Copyright (c) 2015 SUSE LINUX GmbH, Nuernberg, Germany. +# Copyright (c) 2015 SUSE LINUX Products GmbH, Nuernberg, Germany. # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -147,6 +147,8 @@ Source12: grub2-snapper-plugin.sh Source14: 80_suse_btrfs_snapshot Source15: grub2-once.service Source16: grub2-xen.cfg +# required hook for systemd-sleep (bsc#941758) +Source17: grub2-systemd-sleep.sh Source1000: PATCH_POLICY Patch1: rename-grub-info-file-to-grub2.patch Patch2: grub2-linux.patch @@ -386,6 +388,21 @@ BuildArch: noarch %description snapper-plugin Grub2's snapper plugin for advanced btrfs snapshot boot menu management +%if 0%{?has_systemd:1} +%package systemd-sleep-plugin + +Summary: Grub2's systemd-sleep plugin +Group: System/Fhs +Requires: grub2 +Requires: util-linux +Supplements: packageand(systemd:grub2) +BuildArch: noarch + +%description systemd-sleep-plugin +Grub2's systemd-sleep plugin for directly booting hibernated kernel image in +swap partition while in resuming +%endif + %prep # We create (if we build for efi) two copies of the sources in the Builddir %setup -q -n grub-%{version} -a 5 @@ -705,6 +722,7 @@ install -m 755 -D %{SOURCE12} $RPM_BUILD_ROOT%{_libdir}/snapper/plugins/grub install -m 755 -D %{SOURCE14} $RPM_BUILD_ROOT%{_sysconfdir}/grub.d/80_suse_btrfs_snapshot %if 0%{?has_systemd:1} install -m 644 -D %{SOURCE15} $RPM_BUILD_ROOT%{_unitdir}/grub2-once.service +install -m 755 -D %{SOURCE17} $RPM_BUILD_ROOT%{_libdir}/systemd/system-sleep/grub2.sleep %endif %ifarch ppc ppc64 ppc64le @@ -1059,4 +1077,11 @@ fi %{_libdir}/%{name}/%{grubxenarch}/* %endif +%if 0%{?has_systemd:1} +%files systemd-sleep-plugin +%defattr(-,root,root,-) +%dir %{_libdir}/systemd/system-sleep +%{_libdir}/systemd/system-sleep/grub2.sleep +%endif + %changelog