summarylogtreecommitdiffstats
path: root/07-quick_boot.patch
blob: a8e1c82475495a8f617b4cc69337bf64d1fe702c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
diff --git a/configure.ac b/configure.ac
index d3ca0a1..cc2ef03 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1915,6 +1915,17 @@ else
 fi
 AC_SUBST([QUIET_BOOT])
 
+AC_ARG_ENABLE([quick-boot],
+              [AS_HELP_STRING([--enable-quick-boot],
+                              [bypass boot menu if possible (default=no)])],
+              [], [enable_quick_boot=no])
+if test x"$enable_quick_boot" = xyes ; then
+  QUICK_BOOT=1
+else
+  QUICK_BOOT=0
+fi
+AC_SUBST([QUICK_BOOT])
+
 LIBS=""
 
 AC_SUBST([FONT_SOURCE])
diff --git a/docs/grub.texi b/docs/grub.texi
index f8b4b3b..0b58daf 100644
--- a/docs/grub.texi
+++ b/docs/grub.texi
@@ -1563,6 +1563,20 @@ This option may be set to a list of GRUB module names separated by spaces.
 Each module will be loaded as early as possible, at the start of
 @file{grub.cfg}.
 
+@item GRUB_RECORDFAIL_TIMEOUT
+If this option is set, it overrides the default recordfail setting.  A
+setting of -1 causes GRUB to wait for user input indefinitely.  However, a
+false positive in the recordfail mechanism may occur if power is lost during
+boot before boot success is recorded in userspace.  The default setting is
+30, which causes GRUB to wait for user input for thirty seconds before
+continuing.  This default allows interactive users the opportunity to switch
+to a different, working kernel, while avoiding a false positive causing the
+boot to block indefinitely on headless and appliance systems where access to
+a console is restricted or limited.
+
+This option is only effective when GRUB was configured with the
+@option{--enable-quick-boot} option.
+
 @end table
 
 The following options are still accepted for compatibility with existing
diff --git a/grub-core/normal/menu.c b/grub-core/normal/menu.c
index 338f5a5..101aa80 100644
--- a/grub-core/normal/menu.c
+++ b/grub-core/normal/menu.c
@@ -603,6 +603,30 @@ run_menu (grub_menu_t menu, int nested, int *auto_boot)
       static struct grub_term_coordinate *pos;
       int entry = -1;
 
+      if (timeout == 0)
+	{
+	  /* If modifier key statuses can't be detected without a delay,
+	     then a hidden timeout of zero cannot be interrupted in any way,
+	     which is not very helpful.  Bump it to three seconds in this
+	     case to give the user a fighting chance.  */
+	  grub_term_input_t term;
+	  int nterms = 0;
+	  int mods_detectable = 1;
+
+	  FOR_ACTIVE_TERM_INPUTS(term)
+	  {
+	    if (!term->getkeystatus)
+	      {
+		mods_detectable = 0;
+		break;
+	      }
+	    else
+	      nterms++;
+	  }
+	  if (!mods_detectable || !nterms)
+	    timeout = 3;
+	}
+
       if (timeout_style == TIMEOUT_STYLE_COUNTDOWN && timeout)
 	{
 	  pos = grub_term_save_pos ();
diff --git a/util/grub-mkconfig.in b/util/grub-mkconfig.in
index 17269b3..92e640c 100644
--- a/util/grub-mkconfig.in
+++ b/util/grub-mkconfig.in
@@ -253,7 +253,8 @@ export GRUB_DEFAULT \
   GRUB_ENABLE_CRYPTODISK \
   GRUB_BADRAM \
   GRUB_OS_PROBER_SKIP_LIST \
-  GRUB_DISABLE_SUBMENU
+  GRUB_DISABLE_SUBMENU \
+  GRUB_RECORDFAIL_TIMEOUT
 
 if test "x${grub_cfg}" != "x"; then
   rm -f "${grub_cfg}.new"
diff --git a/util/grub.d/00_header.in b/util/grub.d/00_header.in
index c5955df..d660755 100644
--- a/util/grub.d/00_header.in
+++ b/util/grub.d/00_header.in
@@ -21,6 +21,8 @@ prefix="@prefix@"
 exec_prefix="@exec_prefix@"
 datarootdir="@datarootdir@"
 grub_lang=`echo $LANG | cut -d . -f 1`
+grubdir="`echo "/@bootdirname@/@grubdirname@" | sed 's,//*,/,g'`"
+quick_boot="@QUICK_BOOT@"
 
 export TEXTDOMAIN=@PACKAGE@
 export TEXTDOMAINDIR="@localedir@"
@@ -44,6 +46,7 @@ if [ "x${GRUB_TIMEOUT_BUTTON}" = "x" ] ; then GRUB_TIMEOUT_BUTTON="$GRUB_TIMEOUT
 
 cat << EOF
 if [ -s \$prefix/grubenv ]; then
+  set have_grubenv=true
   load_env
 fi
 EOF
@@ -96,7 +99,50 @@ function savedefault {
     save_env saved_entry
   fi
 }
+EOF
+
+if [ "$quick_boot" = 1 ]; then
+    cat <<EOF
+function recordfail {
+  set recordfail=1
+EOF
+
+  check_writable () {
+    abstractions="$(grub-probe --target=abstraction "${grubdir}")"
+    for abstraction in $abstractions; do
+      case "$abstraction" in
+        diskfilter | lvm)
+          cat <<EOF
+  # GRUB lacks write support for $abstraction, so recordfail support is disabled.
+EOF
+          return
+          ;;
+      esac
+    done
+
+    FS="$(grub-probe --target=fs "${grubdir}")"
+    case "$FS" in
+      btrfs | cpiofs | newc | odc | romfs | squash4 | tarfs | zfs)
+	cat <<EOF
+  # GRUB lacks write support for $FS, so recordfail support is disabled.
+EOF
+	return
+	;;
+    esac
+
+    cat <<EOF
+  if [ -n "\${have_grubenv}" ]; then if [ -z "\${boot_once}" ]; then save_env recordfail; fi; fi
+EOF
+  }
+
+  check_writable
 
+  cat <<EOF
+}
+EOF
+fi
+
+cat <<EOF
 function load_video {
 EOF
 if [ -n "${GRUB_VIDEO_BACKEND}" ]; then
@@ -290,10 +336,16 @@ fi
 
 make_timeout ()
 {
+    cat << EOF
+if [ "\${recordfail}" = 1 ] ; then
+  set timeout=${GRUB_RECORDFAIL_TIMEOUT:-30}
+else
+EOF
     if [ "x${3}" != "x" ] ; then
 	timeout="${2}"
 	style="${3}"
-    elif [ "x${1}" != "x" ] && [ "x${1}" != "x0" ] ; then
+    elif [ "x${1}" != "x" ] && \
+	 ([ "$quick_boot" = 1 ] || [ "x${1}" != "x0" ]) ; then
 	# Handle the deprecated GRUB_HIDDEN_TIMEOUT scheme.
 	timeout="${1}"
 	if [ "x${2}" != "x0" ] ; then
@@ -312,26 +364,27 @@ make_timeout ()
 	style="menu"
     fi
     cat << EOF
-if [ x\$feature_timeout_style = xy ] ; then
-  set timeout_style=${style}
-  set timeout=${timeout}
+  if [ x\$feature_timeout_style = xy ] ; then
+    set timeout_style=${style}
+    set timeout=${timeout}
 EOF
     if [ "x${style}" = "xmenu" ] ; then
 	cat << EOF
-# Fallback normal timeout code in case the timeout_style feature is
-# unavailable.
-else
-  set timeout=${timeout}
+  # Fallback normal timeout code in case the timeout_style feature is
+  # unavailable.
+  else
+    set timeout=${timeout}
 EOF
     else
 	cat << EOF
-# Fallback hidden-timeout code in case the timeout_style feature is
-# unavailable.
-elif sleep${verbose} --interruptible ${timeout} ; then
-  set timeout=0
+  # Fallback hidden-timeout code in case the timeout_style feature is
+  # unavailable.
+  elif sleep${verbose} --interruptible ${timeout} ; then
+    set timeout=0
 EOF
     fi
     cat << EOF
+  fi
 fi
 EOF
 }
diff --git a/util/grub.d/10_linux.in b/util/grub.d/10_linux.in
index e0c22fc..9c992c6 100644
--- a/util/grub.d/10_linux.in
+++ b/util/grub.d/10_linux.in
@@ -21,6 +21,7 @@ prefix="@prefix@"
 exec_prefix="@exec_prefix@"
 datarootdir="@datarootdir@"
 quiet_boot="@QUIET_BOOT@"
+quick_boot="@QUICK_BOOT@"
 
 . "$pkgdatadir/grub-mkconfig_lib"
 
@@ -109,6 +110,9 @@ linux_entry ()
   else
       echo "menuentry '$(echo "$os" | grub_quote)' ${CLASS} \$menuentry_id_option 'gnulinux-simple-$boot_device_id' {" | sed "s/^/$submenu_indentation/"
   fi      
+  if [ "$quick_boot" = 1 ]; then
+      echo "	recordfail" | sed "s/^/$submenu_indentation/"
+  fi
   if [ x$type != xrecovery ] ; then
       save_default_entry | grub_add_tab
   fi
diff --git a/util/grub.d/30_os-prober.in b/util/grub.d/30_os-prober.in
index 5984e92..e694e33 100644
--- a/util/grub.d/30_os-prober.in
+++ b/util/grub.d/30_os-prober.in
@@ -20,12 +20,26 @@ set -e
 prefix="@prefix@"
 exec_prefix="@exec_prefix@"
 datarootdir="@datarootdir@"
+quick_boot="@QUICK_BOOT@"
 
 export TEXTDOMAIN=@PACKAGE@
 export TEXTDOMAINDIR="@localedir@"
 
 . "$pkgdatadir/grub-mkconfig_lib"
 
+found_other_os=
+
+adjust_timeout () {
+  if [ "$quick_boot" = 1 ] && [ "x${found_other_os}" != "x" ]; then
+    cat << EOF
+set timeout_style=hidden
+if [ "\${timeout}" = 0 ]; then
+  set timeout=10
+fi
+EOF
+  fi
+}
+
 if [ "x${GRUB_DISABLE_OS_PROBER}" = "xtrue" ]; then
   grub_warn "$(gettext_printf "os-prober will not be executed to detect other bootable partitions.\nSystems on them will not be added to the GRUB boot configuration.\nCheck GRUB_DISABLE_OS_PROBER documentation entry.")"
   exit 0
@@ -45,6 +59,7 @@ if [ -z "${OSPROBED}" ] ; then
 fi
 
 osx_entry() {
+    found_other_os=1
     if [ x$2 = x32 ]; then
         # TRANSLATORS: it refers to kernel architecture (32-bit)
 	bitstr="$(gettext "(32-bit)")"
@@ -149,6 +164,7 @@ for OS in ${OSPROBED} ; do
   case ${BOOT} in
     chain)
 
+      found_other_os=1
 	  onstr="$(gettext_printf "(on %s)" "${DEVICE}")"
       cat << EOF
 menuentry '$(echo "${LONGNAME} $onstr" | grub_quote)' $CLASS --class os \$menuentry_id_option 'osprober-chain-$(grub_get_device_id "${DEVICE}")' {
@@ -179,6 +195,7 @@ EOF
     ;;
     efi)
 
+	found_other_os=1
 	EFIPATH=${DEVICE#*@}
 	DEVICE=${DEVICE%@*}
 	onstr="$(gettext_printf "(on %s)" "${DEVICE}")"
@@ -222,6 +239,7 @@ EOF
 	  LINITRD="${LINITRD#/boot}"
 	fi
 
+	found_other_os=1
 	onstr="$(gettext_printf "(on %s)" "${DEVICE}")"
 	recovery_params="$(echo "${LPARAMS}" | grep single)" || true
 	counter=1
@@ -302,6 +320,7 @@ EOF
       fi
     ;;
     hurd)
+      found_other_os=1
       onstr="$(gettext_printf "(on %s)" "${DEVICE}")"
       cat << EOF
 menuentry '$(echo "${LONGNAME} $onstr" | grub_quote)' --class hurd --class gnu --class os \$menuentry_id_option 'osprober-gnuhurd-/boot/gnumach.gz-false-$(grub_get_device_id "${DEVICE}")' {
@@ -344,3 +363,5 @@ EOF
     ;;
   esac
 done
+
+adjust_timeout