/
grub2.rb
286 lines (236 loc) · 8.49 KB
/
grub2.rb
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
# frozen_string_literal: true
require "yast"
require "y2storage"
require "bootloader/grub2base"
require "bootloader/mbr_update"
require "bootloader/device_map"
require "bootloader/stage1"
require "bootloader/grub_install"
require "bootloader/systeminfo"
Yast.import "Arch"
Yast.import "BootStorage"
Yast.import "HTML"
module Bootloader
# Represents non-EFI variant of GRUB2
class Grub2 < Grub2Base
attr_reader :stage1
attr_reader :device_map
attr_accessor :secure_boot
def initialize
super
textdomain "bootloader"
@stage1 = Stage1.new
@grub_install = GrubInstall.new(efi: false)
@device_map = DeviceMap.new
end
# Read settings from disk, overwritting already set values
def read
super
@secure_boot = Sysconfig.from_system.secure_boot
begin
stage1.read
rescue Errno::ENOENT
# grub_installdevice is not part of grub2 rpm, so it doesn't need to exist.
# In such case ignore exception and use empty @stage1
log.info "grub_installdevice does not exist. Using empty one."
@stage1 = Stage1.new
end
begin
# device map is needed only for legacy boot on intel
device_map.read if Yast::Arch.x86_64 || Yast::Arch.i386
rescue Errno::ENOENT
# device map is only optional part of grub2, so it doesn't need to exist.
# In such case ignore exception and use empty device map
log.info "grub2/device.map does not exist. Using empty one."
@device_map = DeviceMap.new
end
end
# Write bootloader settings to disk
# @return [Boolean] true on success
def write
# super have to called as first as grub install require some config writen in ancestor
super
device_map.write if Yast::Arch.x86_64 || Yast::Arch.i386
# TODO: own class handling PBMR
# set it only for gpt disk bsc#1008092
pmbr_setup(*::Yast::BootStorage.gpt_disks(stage1.devices))
# powernv must not call grub2-install (bnc#970582)
unless Yast::Arch.board_powernv
failed = @grub_install.execute(
devices: stage1.devices, secure_boot: @secure_boot, trusted_boot: trusted_boot
)
failed.each { |f| stage1.remove_device(f) }
stage1.write
end
# Do some mbr activations ( s390 do not have mbr nor boot flag on its disks )
# powernv do not have prep partition, so we do not have any partition to activate (bnc#970582)
MBRUpdate.new.run(stage1) if !Yast::Arch.s390 && !Yast::Arch.board_powernv
end
def propose
super
@secure_boot = Systeminfo.secure_boot_active?
stage1.propose
# for GPT add protective MBR flag otherwise some systems won't
# boot, safer option for legacy booting (bnc#872054)
self.pmbr_action = :add
log.info "proposed pmbr_action #{pmbr_action}."
device_map.propose if Yast::Arch.x86_64 || Yast::Arch.i386
end
def merge(other)
super
@secure_boot = other.secure_boot unless other.secure_boot.nil?
@device_map = other.device_map if !other.device_map.empty?
stage1.merge(other.stage1)
end
# Display bootloader summary
# @return a list of summary lines
def summary(simple_mode: false)
result = [
Yast::Builtins.sformat(
_("Boot Loader Type: %1"),
"GRUB2"
)
]
result << secure_boot_summary if Systeminfo.secure_boot_available?(name)
result << trusted_boot_summary if Systeminfo.trusted_boot_available?(name)
locations_val = locations
if !locations_val.empty?
result << Yast::Builtins.sformat(
_("Status Location: %1"),
locations_val.join(", ")
)
end
# it is necessary different summary for autoyast and installation
# other mode than autoyast on running system
# both ppc and s390 have special devices for stage1 so it do not make sense
# allow change of location to MBR or boot partition (bnc#879107)
no_location = simple_mode || Yast::Arch.ppc || Yast::Arch.s390 || Yast::Mode.config
result << url_location_summary unless no_location
order_sum = disk_order_summary
result << order_sum unless order_sum.empty?
result
end
def name
"grub2"
end
def packages
res = super
res << "grub2"
res << "syslinux" if include_syslinux_package?
res << "trustedgrub2" << "trustedgrub2-i386-pc" if include_trustedgrub2_packages?
res
end
# FIXME: refactor with injection like super(prewrite: prewrite, sysconfig = ...)
# overwrite BootloaderBase version to save trusted boot
def write_sysconfig(prewrite: false)
sysconfig = Bootloader::Sysconfig.new(
bootloader: name, secure_boot: @secure_boot, trusted_boot: trusted_boot
)
prewrite ? sysconfig.pre_write : sysconfig.write
end
private
def secure_boot_summary
_("Secure Boot:") + " " + (@secure_boot ? _("enabled") : _("disabled")) + " " +
if @secure_boot
"<a href=\"disable_secure_boot\">(" + _("disable") + ")</a>"
else
"<a href=\"enable_secure_boot\">(" + _("enable") + ")</a>"
end
end
def trusted_boot_summary
_("Trusted Boot:") + " " + (trusted_boot ? _("enabled") : _("disabled")) + " " +
if trusted_boot
"<a href=\"disable_trusted_boot\">(" + _("disable") + ")</a>"
else
"<a href=\"enable_trusted_boot\">(" + _("enable") + ")</a>"
end
end
# Checks if syslinux package should be included
#
# Needed for generic_mbr binary files, but it must not be required in inst-sys as inst-sys have
# it itself (bsc#1004229).
#
# @return [Boolean] true if syslinux package should be included; false otherwise
def include_syslinux_package?
return false if Yast::Stage.initial
stage1.generic_mbr?
end
# @return [Boolean] true when trustedgrub2 packages should be included; false otherwise
def include_trustedgrub2_packages?
return false unless trusted_boot
Yast::Arch.x86_64 || Yast::Arch.i386
end
def devicegraph
Y2Storage::StorageManager.instance.staging
end
def disk_order_summary
return "" if Yast::Arch.s390
return "" if device_map.size < 2
Yast::Builtins.sformat(
# part of summary, %1 is a list of hard disks device names
_("Order of Hard Disks: %1"),
device_map.disks_order.join(", ")
)
end
def locations
locations = []
partition_location = Yast::BootStorage.boot_partitions.map(&:name).join(", ")
locations << partition_location + _(" (/boot)") if stage1.boot_partition?
if stage1.mbr?
# TRANSLATORS: MBR is acronym for Master Boot Record, if nothing locally specific
# is used in your language, then keep it as it is.
locations << Yast::BootStorage.boot_disks.map(&:name).join(", ") + _(" (MBR)")
end
locations << stage1.custom_devices if !stage1.custom_devices.empty?
locations
end
def mbr_line
if stage1.mbr?
_(
"Install bootcode into MBR (<a href=\"disable_boot_mbr\">do not install</a>)"
)
else
_(
"Do not install bootcode into MBR (<a href=\"enable_boot_mbr\">install</a>)"
)
end
end
def partition_line
if stage1.boot_partition?
_(
"Install boot code into a partition with /boot " \
"(<a href=\"disable_boot_boot\">do not install</a>)"
)
else
_(
"Do not install boot code into a partition with /boot " \
"(<a href=\"enable_boot_boot\">install</a>)"
)
end
end
# FATE#303643 Enable one-click changes in bootloader proposal
#
#
def url_location_summary
log.info "Prepare url summary for GRUB2"
line = +"<ul>\n<li>"
line << mbr_line
line << "</li>\n"
# do not allow to switch on boot from partition that do not support it
if stage1.can_use_boot?
line << "<li>"
line << partition_line
line << "</li>"
end
if stage1.devices.empty?
# no location chosen, so warn user that it is problem unless he is sure
msg = _("Warning: No location for bootloader stage1 selected." \
"Unless you know what you are doing please select above location.")
line << "<li>" << Yast::HTML.Colorize(msg, "red") << "</li>"
end
line << "</ul>"
# TRANSLATORS: title for list of location proposals
_("Change Location: %s") % line
end
end
end