• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

yast / yast-bootloader / 11817815252

13 Nov 2024 01:01PM UTC coverage: 87.695%. Remained the same
11817815252

push

github

schubi2
grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

rubocop

rubocop

grub2bls

grub2bls

grub2bls

sync text about secure boot on s390 to be correct (bsc#1219989)

make rubocop happy

rubocop

rubocop

fixed test cases

fixed test cases

grub2bls

grub2bls

grub2bls

grub2bls

grub2bls

rubocop

rubocop

rubocop

rubocop

rubocop

docu

added yast-bootloader

added yast-bootloader

93 of 100 new or added lines in 7 files covered. (93.0%)

64 existing lines in 6 files now uncovered.

3371 of 3844 relevant lines covered (87.7%)

13.03 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

87.9
/src/lib/bootloader/grub2_widgets.rb
1
# frozen_string_literal: true
2

3
require "yast"
1✔
4

5
require "bootloader/generic_widgets"
1✔
6
require "bootloader/device_map_dialog"
1✔
7
require "bootloader/serial_console"
1✔
8
require "bootloader/cpu_mitigations"
1✔
9
require "bootloader/systeminfo"
1✔
10
require "bootloader/os_prober"
1✔
11
require "bootloader/device_path"
1✔
12
require "cfa/matcher"
1✔
13

14
Yast.import "BootStorage"
1✔
15
Yast.import "Initrd"
1✔
16
Yast.import "Label"
1✔
17
Yast.import "Report"
1✔
18
Yast.import "UI"
1✔
19
Yast.import "Mode"
1✔
20
Yast.import "Arch"
1✔
21

22
module Bootloader
1✔
23
  module Grub2Widget
1✔
24
    # Adds to generic widget grub2 specific helpers
25
    module Grub2Helper
1✔
26
      def grub_default
1✔
27
        BootloaderFactory.current.grub_default
72✔
28
      end
29

30
      def stage1
1✔
31
        BootloaderFactory.current.stage1
9✔
32
      end
33

34
      def password
1✔
35
        BootloaderFactory.current.password
36✔
36
      end
37

38
      def sections
1✔
39
        BootloaderFactory.current.sections
3✔
40
      end
41

42
      def grub2
1✔
43
        BootloaderFactory.current
23✔
44
      end
45
    end
46

47
    # Represents bootloader timeout value
48
    class TimeoutWidget < CWM::IntField
1✔
49
      include Grub2Helper
1✔
50

51
      def initialize(hidden_menu_widget)
1✔
52
        textdomain "bootloader"
11✔
53

54
        super()
11✔
55

56
        @minimum = -1
11✔
57
        @maximum = 600
11✔
58
        @hidden_menu_widget = hidden_menu_widget
11✔
59
      end
60

61
      attr_reader :minimum, :maximum
1✔
62

63
      def label
1✔
64
        _("&Timeout in Seconds")
1✔
65
      end
66

67
      def help
1✔
68
        _("<p><b>Timeout in Seconds</b>\n" \
1✔
69
          "specifies the time the boot loader will wait until the default kernel is loaded.</p>\n")
70
      end
71

72
      def init
1✔
73
        self.value = if grub_default.hidden_timeout && grub_default.hidden_timeout.to_i > 0
2✔
74
          grub_default.hidden_timeout.to_i
1✔
75
        else
76
          grub_default.timeout.to_i
1✔
77
        end
78
      end
79

80
      def store
1✔
81
        if @hidden_menu_widget.is_a?(CWM::Empty)
4✔
NEW
82
          grub_default.timeout = value.to_s
×
83
        elsif @hidden_menu_widget.checked?
4✔
84
          grub_default.hidden_timeout = value.to_s
2✔
85
          grub_default.timeout = "0"
2✔
86
        else
87
          grub_default.hidden_timeout = "0"
2✔
88
          grub_default.timeout = value.to_s
2✔
89
        end
90
      end
91
    end
92

93
    # Represents decision if bootloader need activated partition
94
    class ActivateWidget < CWM::CheckBox
1✔
95
      include Grub2Helper
1✔
96

97
      def initialize
1✔
98
        textdomain "bootloader"
5✔
99

100
        super
5✔
101
      end
102

103
      def label
1✔
104
        _("Set &active Flag in Partition Table for Boot Partition")
1✔
105
      end
106

107
      def help
1✔
108
        _(
1✔
109
          "<p><b>Set Active Flag in Partition Table for Boot Partition</b>\n" \
110
          "specifies whether the partition containing " \
111
          "the boot loader will have the \"active\" flag." \
112
          " The generic MBR code will then\n" \
113
          "boot the active partition. Older BIOSes require one partition to be active even\n" \
114
          "if the boot loader is installed in the MBR.</p>"
115
        )
116
      end
117

118
      def init
1✔
119
        self.value = stage1.activate?
1✔
120
      end
121

122
      def store
1✔
123
        stage1.activate = checked?
1✔
124
      end
125
    end
126

127
    # Represents decision if generic MBR have to be installed on disk
128
    class GenericMBRWidget < CWM::CheckBox
1✔
129
      include Grub2Helper
1✔
130

131
      def initialize
1✔
132
        textdomain "bootloader"
5✔
133

134
        super
5✔
135
      end
136

137
      def label
1✔
138
        _("Write &generic Boot Code to MBR")
1✔
139
      end
140

141
      def help
1✔
142
        _(
1✔
143
          "<p><b>Write generic Boot Code to MBR</b> replace the master boot" \
144
          " record of your disk with generic code (OS independent code which\n" \
145
          "boots the active partition).</p>"
146
        )
147
      end
148

149
      def init
1✔
150
        self.value = stage1.generic_mbr?
1✔
151
      end
152

153
      def store
1✔
154
        stage1.generic_mbr = checked?
1✔
155
      end
156
    end
157

158
    # Represents decision if menu should be hidden or visible
159
    class HiddenMenuWidget < CWM::CheckBox
1✔
160
      include Grub2Helper
1✔
161

162
      def initialize
1✔
163
        textdomain "bootloader"
4✔
164

165
        super
4✔
166
      end
167

168
      def label
1✔
169
        _("&Hide Menu on Boot")
1✔
170
      end
171

172
      def help
1✔
173
        _(
1✔
174
          "<p>Selecting <b>Hide Menu on Boot</b> will hide the boot menu.</p>"
175
        )
176
      end
177

178
      def init
1✔
179
        self.value = grub_default.hidden_timeout && grub_default.hidden_timeout.to_i > 0
1✔
180
      end
181
    end
182

183
    # Represents if os prober should be run
184
    class OSProberWidget < CWM::CheckBox
1✔
185
      include Grub2Helper
1✔
186

187
      def initialize
1✔
188
        textdomain "bootloader"
5✔
189

190
        super
5✔
191
      end
192

193
      def label
1✔
194
        _("Pro&be Foreign OS")
1✔
195
      end
196

197
      def help
1✔
198
        _(
1✔
199
          "<p><b>Probe Foreign OS</b> by means of os-prober for multiboot with " \
200
          "other foreign distribution </p>"
201
        )
202
      end
203

204
      def init
1✔
205
        self.value = grub_default.os_prober.enabled?
1✔
206
      end
207

208
      def store
1✔
209
        grub_default.os_prober.value = checked?
1✔
210
      end
211
    end
212

213
    # Represents Protective MBR action
214
    class PMBRWidget < CWM::ComboBox
1✔
215
      include Grub2Helper
1✔
216

217
      def initialize
1✔
218
        textdomain "bootloader"
6✔
219

220
        super
6✔
221
      end
222

223
      def label
1✔
224
        _("&Protective MBR flag")
1✔
225
      end
226

227
      def help
1✔
228
        _(
1✔
229
          "<p><b>Protective MBR flag</b> is expert only settings, that is needed " \
230
          "only on exotic hardware. For details see Protective MBR in GPT disks. " \
231
          "Do not touch if you are not sure.</p>"
232
        )
233
      end
234

235
      def init
1✔
236
        self.value = grub2.pmbr_action
1✔
237
      end
238

239
      def items
1✔
240
        [
241
          # TRANSLATORS: set flag on disk
242
          [:add, _("set")],
1✔
243
          # TRANSLATORS: remove flag from disk
244
          [:remove, _("remove")],
245
          # TRANSLATORS: do not change flag on disk
246
          [:nothing, _("do not change")]
247
        ]
248
      end
249

250
      def store
1✔
251
        grub2.pmbr_action = value
1✔
252
      end
253
    end
254

255
    # Represents switcher for secure boot on EFI
256
    class SecureBootWidget < CWM::CheckBox
1✔
257
      include Grub2Helper
1✔
258

259
      def initialize
1✔
260
        textdomain "bootloader"
4✔
261

262
        super
4✔
263
      end
264

265
      def label
1✔
266
        _("&Secure Boot Support")
1✔
267
      end
268

269
      def help
1✔
270
        if Yast::Arch.s390
1✔
UNCOV
271
          _(
×
272
            "<p><b>Secure Boot Support</b> if checked enables Secure Boot support.<br>" \
273
            "This does not turn on secure booting. " \
274
            "It only switches to the new secure-boot enabled boot data format. " \
275
            "Note that this new format works only on z15 or later and "\
276
            "only for some disk types. " \
277
            "For more details see the requirements at  " \
278
            "https://www.ibm.com/docs/en/linux-on-systems?topic=introduction-requirements</p>"
279
          )
280
        else
281
          _(
1✔
282
            "<p><b>Secure Boot Support</b> if checked enables Secure Boot support.<br>" \
283
            "This does not turn on secure booting. " \
284
            "It only sets up the boot loader in a way that supports secure booting. " \
285
            "You still have to enable Secure Boot in the UEFI Firmware.</p> "
286
          )
287
        end
288
      end
289

290
      def init
1✔
291
        self.value = grub2.secure_boot
1✔
292
      end
293

294
      def store
1✔
295
        grub2.secure_boot = value
1✔
296
      end
297

298
      def validate
1✔
UNCOV
299
        return true if Yast::Mode.config ||
×
300
          !Yast::Arch.s390 ||
301
          !value ||
302
          value == Systeminfo.secure_boot_active?
303

UNCOV
304
        Yast::Popup.ContinueCancel(
×
305
          # TRANSLATORS: IPL stands for Initial Program Load, IBM speak for system boot
306
          _(
307
            "Secure boot IPL has the following minimum system requirements,\n" \
308
            "depending on the boot device to be IPLed:\n" \
309
            "NVMe disk: IBM LinuxONE III or newer.\n" \
310
            "FC-attached SCSI disk: IBM LinuxONE III, IBM z15 or newer.\n" \
311
            "ECKD DASD with CDL layout: IBM z16, LinuxONE 4 or newer.\n" \
312
            "If these requirements are not met, the system can be IPLed in non-secure mode only."
313
          )
314
        )
315
      end
316
    end
317

318
    # Represents switcher for Trusted Boot
319
    class TrustedBootWidget < CWM::CheckBox
1✔
320
      include Grub2Helper
1✔
321

322
      def initialize
1✔
323
        textdomain "bootloader"
8✔
324

325
        super
8✔
326
      end
327

328
      def label
1✔
329
        _("&Trusted Boot Support")
2✔
330
      end
331

332
      def help
1✔
333
        res = _("<p><b>Trusted Boot</b> " \
2✔
334
                "means measuring the integrity of the boot process,\n" \
335
                "with the help from the hardware (a TPM, Trusted Platform Module,\n" \
336
                "chip).\n")
337
        if grub2.name == "grub2"
2✔
338
          res += _("First you need to make sure Trusted Boot is enabled in the BIOS\n" \
1✔
339
                   "setup (the setting may be named \"Security Chip\", for example).\n")
340
        end
341

342
        res += "</p>"
2✔
343

344
        res
2✔
345
      end
346

347
      def init
1✔
UNCOV
348
        self.value = grub2.trusted_boot
×
349
      end
350

351
      def store
1✔
UNCOV
352
        grub2.trusted_boot = value
×
353
      end
354

355
      def validate
1✔
356
        return true if Yast::Mode.config || !value || ["grub2-efi",
2✔
357
                                                       "grub2-bls"].include?(grub2.name)
358

359
        tpm_files = Dir.glob("/sys/**/pcrs")
1✔
360
        if !tpm_files.empty? && !File.read(tpm_files[0], 1).nil?
1✔
361
          # check for file size does not work, since FS reports it 4096
362
          # even if the file is in fact empty and a single byte cannot
363
          # be read, therefore testing real reading (details: bsc#994556)
UNCOV
364
          return true
×
365
        end
366

367
        Yast::Popup.ContinueCancel(_("Trusted Platform Module not found.\n" \
1✔
368
                                     "Make sure it is enabled in BIOS.\n" \
369
                                     "The system will not boot otherwise."))
370
      end
371
    end
372

373
    # Represents switcher for NVRAM update
374
    class UpdateNvramWidget < CWM::CheckBox
1✔
375
      include Grub2Helper
1✔
376

377
      def initialize
1✔
378
        textdomain "bootloader"
6✔
379

380
        super
6✔
381
      end
382

383
      def label
1✔
384
        _("Update &NVRAM Entry")
1✔
385
      end
386

387
      def help
1✔
388
        _("<p><b>Update NVRAM Entry</b> will add nvram entry for the bootloader\n" \
1✔
389
          "in the firmware.\n" \
390
          "This is usually desirable unless you want to preserve specific settings\n" \
391
          "or need to work around firmware issues.</p>\n")
392
      end
393

394
      def init
1✔
395
        self.value = grub2.update_nvram
2✔
396
      end
397

398
      def store
1✔
399
        grub2.update_nvram = value
2✔
400
      end
401
    end
402

403
    # Represents grub password protection widget
404
    class GrubPasswordWidget < CWM::CustomWidget
1✔
405
      include Grub2Helper
1✔
406

407
      def initialize
1✔
408
        textdomain "bootloader"
25✔
409

410
        super
25✔
411
      end
412

413
      MASKED_PASSWORD = "**********"
1✔
414

415
      def contents
1✔
416
        HBox(
1✔
417
          CheckBoxFrame(
418
            Id(:use_pas),
419
            _("Prot&ect Boot Loader with Password"),
420
            true,
421
            VBox(
422
              HBox(
423
                HSpacing(2),
424
                # TRANSLATORS: checkbox entry
425
                CheckBox(Id(:unrestricted_pw), _("P&rotect Entry Modification Only")),
426
                HStretch()
427
              ),
428
              HBox(
429
                HSpacing(2),
430
                # TRANSLATORS: text entry, please keep it short
431
                Password(Id(:pw1), Opt(:hstretch), _("&Password for GRUB2 User 'root'")),
432
                # text entry
433
                HSpacing(2),
434
                Password(Id(:pw2), Opt(:hstretch), _("Re&type Password")),
435
                HStretch()
436
              )
437
            )
438
          )
439
        )
440
      end
441

442
      def validate
1✔
443
        return true unless Yast::UI.QueryWidget(Id(:use_pas), :Value)
4✔
444

445
        if Yast::UI.QueryWidget(Id(:pw1), :Value) == ""
3✔
446
          Yast::Report.Error(_("The password must not be empty."))
1✔
447
          Yast::UI.SetFocus(Id(:pw1))
1✔
448
          return false
1✔
449
        end
450
        if Yast::UI.QueryWidget(Id(:pw1), :Value) == Yast::UI.QueryWidget(Id(:pw2), :Value)
2✔
451
          return true
1✔
452
        end
453

454
        Yast::Report.Error(_(
1✔
455
                             "'Password' and 'Retype password'\ndo not match. Retype the password."
456
                           ))
457
        Yast::UI.SetFocus(Id(:pw1))
1✔
458
        false
1✔
459
      end
460

461
      def init
1✔
462
        enabled = password.used?
9✔
463
        # read state on disk only if not already set by user (bnc#900026)
464
        value = (enabled && password.password?) ? MASKED_PASSWORD : ""
9✔
465

466
        Yast::UI.ChangeWidget(Id(:use_pas), :Value, enabled)
9✔
467
        Yast::UI.ChangeWidget(Id(:pw1), :Enabled, enabled)
9✔
468
        Yast::UI.ChangeWidget(Id(:pw1), :Value, value)
9✔
469
        Yast::UI.ChangeWidget(Id(:pw2), :Enabled, enabled)
9✔
470
        Yast::UI.ChangeWidget(Id(:pw2), :Value, value)
9✔
471
        Yast::UI.ChangeWidget(Id(:unrestricted_pw), :Enabled, enabled)
9✔
472
        Yast::UI.ChangeWidget(Id(:unrestricted_pw), :Value, password.unrestricted?)
9✔
473
      end
474

475
      def handle(event)
1✔
476
        return unless event["ID"] == :use_pas
3✔
477

478
        enabled = Yast::UI.QueryWidget(Id(:use_pas), :Value)
2✔
479
        Yast::UI.ChangeWidget(Id(:unrestricted_pw), :Enabled, enabled)
2✔
480
        Yast::UI.ChangeWidget(Id(:pw1), :Enabled, enabled)
2✔
481
        Yast::UI.ChangeWidget(Id(:pw2), :Enabled, enabled)
2✔
482

483
        nil
484
      end
485

486
      def store
1✔
487
        usepass = Yast::UI.QueryWidget(Id(:use_pas), :Value)
6✔
488
        matcher = CFA::Matcher.new(key: "rd.shell")
6✔
489
        grub_default.kernel_params.remove_parameter(matcher)
6✔
490
        if !usepass
6✔
491
          password.used = false
2✔
492
          return
2✔
493
        end
494

495
        password.used = true
4✔
496

497
        value = Yast::UI.QueryWidget(Id(:pw1), :Value)
4✔
498
        # special value as we do not know password, so it mean user do not change it
499
        password.password = value if value != MASKED_PASSWORD
4✔
500

501
        value = Yast::UI.QueryWidget(Id(:unrestricted_pw), :Value)
4✔
502
        grub_default.kernel_params.add_parameter("rd.shell", "0") if value
4✔
503
        password.unrestricted = value
4✔
504
      end
505

506
      def help
1✔
507
        _(
1✔
508
          "<p><b>Protect Boot Loader with Password</b>\n" \
509
          "at boot time, modifying or even booting any entry will require the" \
510
          " password. If <b>Protect Entry Modification Only</b> is checked then " \
511
          "booting any entry is not restricted but modifying entries requires " \
512
          "the password (which is the way GRUB 1 behaved). As side-effect of " \
513
          "this option, rd.shell=0 is added to kernel parameters, to prevent " \
514
          "an unauthorized access to the initrd shell. " \
515
          "YaST will only accept the password if you repeat it in " \
516
          "<b>Retype Password</b>. The password applies to the GRUB2 user 'root' " \
517
          "which is distinct from the Linux 'root'. YaST currently does not support " \
518
          "other GRUB2 users. If you need them, use a separate GRUB2 script.</p>"
519
        )
520
      end
521
    end
522

523
    # Represents graphical and serial console setup for bootloader
524
    #
525
    # Allows to configure terminal for grub. It can configure grub
526
    # to use either graphical terminal, console or console over serial line.
527
    #
528
    # Graphical or serial terminal has to be selected explicitly. Either
529
    # one of them or both at once.
530
    # Native console is configured as a fallback when nothing else is selected.
531
    class ConsoleWidget < CWM::CustomWidget
1✔
532
      include Grub2Helper
1✔
533

534
      def initialize
1✔
535
        textdomain "bootloader"
21✔
536

537
        super
21✔
538
      end
539

540
      def contents
1✔
541
        VBox(
1✔
542
          graphical_console_frame,
543
          serial_console_frame
544
        )
545
      end
546

547
      def help
1✔
548
        # Translators: do not translate the quoted parts like "unit"
UNCOV
549
        _(
×
550
          "<p><b>Graphical console</b> when checked it allows to use various " \
551
          "display resolutions. The <tt>auto</tt> option tries to find " \
552
          "the best one when booting starts.</p>\n" \
553
          "<p><b>Serial console</b> when checked it redirects the boot output " \
554
          "to a serial device like <tt>ttyS0</tt>. " \
555
          "At least the <tt>--unit</tt> option has to be specified, " \
556
          "and the complete syntax is <tt>%s</tt>. " \
557
          "Other parts are optional and if not set, a default is used. " \
558
          "<tt>NUM</tt> in commands stands for a positive number like 8. " \
559
          "Example parameters are <tt>serial --speed=38400 --unit=0</tt>.</p>"
560
        ) % syntax
561
      end
562

563
      def init
1✔
564
        init_console
6✔
565
        init_gfxterm
5✔
566

567
        Yast::UI.ChangeWidget(Id(:theme), :Value, grub_default.theme || "")
5✔
568
      rescue RuntimeError
569
        raise ::Bootloader::UnsupportedOption, "GRUB_TERMINAL"
1✔
570
      end
571

572
      def validate
1✔
573
        if Yast::UI.QueryWidget(Id(:console_frame), :Value)
4✔
574
          console_value = Yast::UI.QueryWidget(Id(:console_args), :Value)
3✔
575
          if console_value.strip.empty?
3✔
576
            Yast::Report.Error(
1✔
577
              _("To enable serial console you must provide the corresponding arguments.")
578
            )
579
            Yast::UI.SetFocus(Id(:console_args))
1✔
580
            return false
1✔
581
          end
582
          if ::Bootloader::SerialConsole.load_from_console_args(console_value).nil?
2✔
583
            # Translators: do not translate "unit"
584
            msg = _("To enable the serial console you must provide the corresponding arguments.\n" \
1✔
585
                    "The \"unit\" argument is required, the complete syntax is:\n%s") % syntax
586
            Yast::Report.Error(msg)
1✔
587
            Yast::UI.SetFocus(Id(:console_args))
1✔
588
            return false
1✔
589
          end
590
        end
591
        true
2✔
592
      end
593

594
      def store
1✔
595
        use_serial = Yast::UI.QueryWidget(Id(:console_frame), :Value)
6✔
596
        use_gfxterm = Yast::UI.QueryWidget(Id(:gfxterm_frame), :Value)
6✔
597
        use_console = !use_serial && !use_gfxterm
6✔
598

599
        grub_default.terminal = []
6✔
600
        grub_default.terminal = [:gfxterm] if use_gfxterm
6✔
601

602
        if use_serial
6✔
603
          console_value = Yast::UI.QueryWidget(Id(:console_args), :Value)
2✔
604
          BootloaderFactory.current.enable_serial_console(console_value)
2✔
605
        elsif use_console
4✔
606
          grub_default.terminal = [:console]
3✔
607
        end
608

609
        mode = Yast::UI.QueryWidget(Id(:gfxmode), :Value)
6✔
610
        grub_default.gfxmode = mode if mode != ""
6✔
611

612
        theme = Yast::UI.QueryWidget(Id(:theme), :Value)
6✔
613
        grub_default.theme = theme if theme != ""
6✔
614
      end
615

616
      def handle(event)
1✔
617
        return if event["ID"] != :browsegfx
3✔
618

619
        theme_dir = "/boot/grub2/themes/openSUSE"
2✔
620
        theme_dir = "/boot/grub2" unless ::Dir.exist?(theme_dir)
2✔
621

622
        file = Yast::UI.AskForExistingFile(
2✔
623
          theme_dir,
624
          "*.txt",
625
          _("Choose new graphical theme file")
626
        )
627

628
        Yast::UI.ChangeWidget(Id(:theme), :Value, file) if file
2✔
629

630
        nil
631
      end
632

633
    private
1✔
634

635
      # Initializates serial console specific widgets
636
      def init_console
1✔
637
        enable = grub_default.terminal.include?(:serial) if grub_default.terminal
6✔
638
        Yast::UI.ChangeWidget(Id(:console_frame), :Value, enable)
5✔
639
        args = grub_default.serial_console || ""
5✔
640
        Yast::UI.ChangeWidget(Id(:console_args), :Value, args)
5✔
641
      end
642

643
      # Initializates gfxterm specific widgets
644
      def init_gfxterm
1✔
645
        enable = grub_default.terminal.include?(:gfxterm) if grub_default.terminal
5✔
646
        Yast::UI.ChangeWidget(Id(:gfxterm_frame), :Value, enable)
5✔
647

648
        Yast::UI.ChangeWidget(Id(:gfxmode), :Items, vga_modes_items)
5✔
649
        mode = grub_default.gfxmode
5✔
650

651
        # there's mode specified, use it
652
        Yast::UI.ChangeWidget(Id(:gfxmode), :Value, mode) if mode && mode != ""
5✔
653
      end
654

655
      # Explanation for help and error messages
656
      def syntax
1✔
657
        # Translators: NUM is an abbreviation for "number",
658
        # to be substituted in a command like
659
        # "serial --unit=NUM --speed=NUM --parity={odd|even|no} --word=NUM --stop=NUM"
660
        # so do not use punctuation
661
        n = _("NUM")
1✔
662
        "serial --unit=#{n} --speed=#{n} --parity={odd|even|no} --word=#{n} --stop=#{n}"
1✔
663
      end
664

665
      def graphical_console_frame
1✔
666
        CheckBoxFrame(
1✔
667
          Id(:gfxterm_frame),
668
          _("&Graphical console"),
669
          true,
670
          HBox(
671
            HSpacing(2),
672
            ComboBox(
673
              Id(:gfxmode), Opt(:editable, :hstretch), _("&Console resolution")
674
            ),
675
            HBox(
676
              Left(
677
                InputField(
678
                  Id(:theme), Opt(:hstretch), _("&Console theme")
679
                )
680
              ),
681
              VBox(
682
                Left(Label("")),
683
                Left(
684
                  PushButton(Id(:browsegfx), Opt(:notify), Yast::Label.BrowseButton)
685
                )
686
              )
687
            ),
688
            HStretch()
689
          )
690
        )
691
      end
692

693
      def vga_modes_items
1✔
694
        return @vga_modes if @vga_modes
5✔
695

696
        @vga_modes = Yast::Initrd.VgaModes
5✔
697

698
        @vga_modes.sort! do |a, b|
5✔
699
          res = a["width"] <=> b["width"]
285✔
700
          res = a["height"] <=> b["height"] if res.zero?
285✔
701

702
          res
285✔
703
        end
704

705
        @vga_modes.map! { |a| "#{a["width"]}x#{a["height"]}" }
135✔
706
        @vga_modes.uniq!
5✔
707

708
        @vga_modes.map! { |m| Item(Id(m), m) }
50✔
709
        @vga_modes.unshift(Item(Id("auto"), _("Autodetect by grub2")))
5✔
710

711
        @vga_modes
5✔
712
      end
713

714
      def serial_console_frame
1✔
715
        CheckBoxFrame(
1✔
716
          Id(:console_frame),
717
          _("&Serial console"),
718
          true,
719
          HBox(
720
            HSpacing(2),
721
            InputField(
722
              Id(:console_args),
723
              Opt(:hstretch),
724
              _("&Console arguments")
725
            ),
726
            HStretch()
727
          )
728
        )
729
      end
730
    end
731

732
    # represent choosing default section to boot
733
    class DefaultSectionWidget < CWM::ComboBox
1✔
734
      include Grub2Helper
1✔
735

736
      def initialize
1✔
737
        textdomain "bootloader"
6✔
738

739
        super
6✔
740
      end
741

742
      def label
1✔
743
        _("&Default Boot Section")
1✔
744
      end
745

746
      def help
1✔
747
        _(
1✔
748
          "<p><b>Default Boot Section</b> selects the default section for booting.\n" \
749
          " If sections are not generated yet ( e.g. during installation) \n" \
750
          "then the box is empty and the default is picked by grub2 itself.</p>\n"
751
        )
752
      end
753

754
      def init
1✔
755
        self.value = sections.default
1✔
756
      end
757

758
      def items
1✔
759
        sections.all.map do |section|
1✔
760
          [section, section]
2✔
761
        end
762
      end
763

764
      def store
1✔
765
        sections.default = value
1✔
766
      end
767
    end
768

769
    # Represents stage1 location for bootloader
770
    class LoaderLocationWidget < CWM::CustomWidget
1✔
771
      include Grub2Helper
1✔
772

773
      def contents
1✔
774
        textdomain "bootloader"
1✔
775

776
        VBox(
1✔
777
          Frame(
778
            _("Boot Code Location"),
779
            HBox(
780
              HSpacing(1),
781
              VBox(*location_checkboxes),
782
              HSpacing(1)
783
            )
784
          ),
785
          VSpacing(1)
786
        )
787
      end
788

789
      def handle(event)
1✔
790
        return unless event["ID"] == :custom
1✔
791

792
        checked = Yast::UI.QueryWidget(Id(:custom), :Value)
×
UNCOV
793
        Yast::UI.ChangeWidget(Id(:custom_list), :Enabled, checked)
×
794

795
        nil
796
      end
797

798
      def init
1✔
799
        if locations.include?(:boot)
×
UNCOV
800
          Yast::UI.ChangeWidget(Id(:boot), :Value,
×
801
            stage1.boot_partition?)
802
        end
803
        if locations.include?(:logical)
×
UNCOV
804
          Yast::UI.ChangeWidget(Id(:logical), :Value, stage1.boot_partition?)
×
805
        end
806
        if locations.include?(:extended)
×
UNCOV
807
          Yast::UI.ChangeWidget(Id(:extended), :Value, stage1.extended_boot_partition?)
×
808
        end
UNCOV
809
        Yast::UI.ChangeWidget(Id(:mbr), :Value, stage1.mbr?) if locations.include?(:mbr)
×
810

UNCOV
811
        init_custom_devices(stage1.custom_devices)
×
812
      end
813

814
      def store
1✔
815
        stage1.clear_devices
×
UNCOV
816
        locations.each { |l| add_location(l) }
×
817

UNCOV
818
        return unless Yast::UI.QueryWidget(:custom, :Value)
×
819

820
        devs = Yast::UI.QueryWidget(:custom_list, :Value)
×
821
        devs.split(",").each do |dev|
×
UNCOV
822
          stage1.add_device(DevicePath.new(dev).path)
×
823
        end
824
      end
825

826
      def validate
1✔
827
        return true if !Yast::UI.QueryWidget(:custom, :Value)
1✔
828

UNCOV
829
        devs = Yast::UI.QueryWidget(:custom_list, :Value)
×
830

831
        if devs.strip.empty?
×
832
          Yast::Report.Error(_("Custom boot device has to be specified if checked"))
×
833
          Yast::UI.SetFocus(Id(:custom_list))
×
UNCOV
834
          return false
×
835
        end
836

837
        invalid_devs = invalid_custom_devices(devs)
×
838
        if !invalid_devs.empty?
×
UNCOV
839
          ret = Yast::Popup.ContinueCancel(
×
840
            format(
841
              _(
842
                "These custom devices can be invalid: %s." \
843
                "Please check if exist and spelled correctly." \
844
                "Do you want to continue?"
845
              ),
846
              invalid_devs.join(", ")
847
            )
848
          )
849

850
          if !ret
×
851
            Yast::UI.SetFocus(Id(:custom_list))
×
UNCOV
852
            return false
×
853
          end
854
        end
855

UNCOV
856
        true
×
857
      end
858

859
    private
1✔
860

861
      def add_location(id)
1✔
UNCOV
862
        return unless Yast::UI.QueryWidget(Id(id), :Value)
×
863

UNCOV
864
        case id
×
865
        when :boot, :logical
UNCOV
866
          stage1.boot_partition_names.each { |d| stage1.add_udev_device(d) }
×
867
        when :extended
UNCOV
868
          stage1.extended_boot_partitions_names.each { |d| stage1.add_udev_device(d) }
×
869
        when :mbr
UNCOV
870
          stage1.boot_disk_names.each { |d| stage1.add_udev_device(d) }
×
871
        end
872
      end
873

874
      def init_custom_devices(custom_devices)
1✔
875
        if custom_devices.empty?
×
876
          Yast::UI.ChangeWidget(:custom, :Value, false)
×
UNCOV
877
          Yast::UI.ChangeWidget(:custom_list, :Enabled, false)
×
878
        else
879
          Yast::UI.ChangeWidget(:custom, :Value, true)
×
880
          Yast::UI.ChangeWidget(:custom_list, :Enabled, true)
×
UNCOV
881
          Yast::UI.ChangeWidget(:custom_list, :Value, custom_devices.join(","))
×
882
        end
883
      end
884

885
      # Checks list of custom devices
886
      #
887
      # @param devs_list[String] comma separated list of device definitions
888
      #
889
      # @return [Array<String>] devices which didn't pass validation
890
      def invalid_custom_devices(devs_list)
1✔
891
        # almost any byte sequence is potentially valid path in unix like systems
892
        # AY profile can be generated for whatever system so we cannot decite if
893
        # particular byte sequence is valid or not
UNCOV
894
        return [] if Yast::Mode.config
×
895

896
        devs_list.split(",").reject do |d|
×
UNCOV
897
          dev_path = DevicePath.new(d)
×
898

UNCOV
899
          if Yast::Mode.installation
×
900
            # uuids are generated later by mkfs, so not known in time of installation
901
            # so whatever can be true
UNCOV
902
            dev_path.uuid? || dev_path.valid?
×
903
          else
UNCOV
904
            dev_path.valid?
×
905
          end
906
        end
907
      end
908

909
      def locations
1✔
910
        @locations ||= stage1.available_locations
4✔
911
      end
912

913
      def location_checkboxes
1✔
914
        checkboxes = []
1✔
915
        # TRANSLATORS: %s is used to specify exact devices
916
        add_checkbox(checkboxes, :boot,
1✔
917
          format(_("Wri&te to Partition (%s)"), stage1.boot_partition_names.join(", ")))
918
        # TRANSLATORS: %s is used to specify exact devices
919
        add_checkbox(checkboxes, :logical,
1✔
920
          format(_("Wri&te to Logical Partition (%s)"), stage1.boot_partition_names.join(", ")))
921
        # TRANSLATORS: %s is used to specify exact devices
922
        add_checkbox(checkboxes, :extended,
1✔
923
          format(_("Write to &Extended Partition (%s)"),
924
            stage1.extended_boot_partitions_names.join(", ")))
925
        # TRANSLATORS: %s is used to specify exact devices
926
        add_checkbox(checkboxes, :mbr,
1✔
927
          format(_("Write to &Master Boot Record (%s)"), stage1.boot_disk_names.join(", ")))
928

929
        checkboxes.concat(custom_partition_content)
1✔
930
      end
931

932
      def add_checkbox(checkboxes, id, title)
1✔
933
        checkboxes << Left(CheckBox(Id(id), title)) if locations.include?(id)
4✔
934
      end
935

936
      def custom_partition_content
1✔
937
        [
938
          Left(CheckBox(Id(:custom), Opt(:notify), _("C&ustom Boot Partition"))),
1✔
939
          Left(InputField(Id(:custom_list), Opt(:hstretch), ""))
940
        ]
941
      end
942
    end
943

944
    # Represents button that open Device Map edit dialog
945
    class DeviceMapWidget < ::CWM::PushButton
1✔
946
      include Grub2Helper
1✔
947

948
      def label
1✔
949
        textdomain "bootloader"
1✔
950

951
        _("&Edit Disk Boot Order")
1✔
952
      end
953

954
      def help
1✔
955
        textdomain "bootloader"
1✔
956

957
        _(
1✔
958
          "<p><b>Edit Disk Boot Order</b>\n" \
959
          "allows to specify the order of the disks according to the order in BIOS. Use\n" \
960
          "the <b>Up</b> and <b>Down</b> buttons to reorder the disks.\n" \
961
          "To add a disk, push <b>Add</b>.\n" \
962
          "To remove a disk, push <b>Remove</b>.</p>"
963
        )
964
      end
965

966
      def handle
1✔
967
        DeviceMapDialog.run(grub2.device_map)
1✔
968

969
        nil
970
      end
971
    end
972

973
    # represents Tab with kernel related configuration
974
    class KernelTab < CWM::Tab
1✔
975
      include Grub2Helper
1✔
976

977
      def label
1✔
978
        textdomain "bootloader"
1✔
979

980
        _("&Kernel Parameters")
1✔
981
      end
982

983
      def contents
1✔
984
        console_widget = if Yast::Arch.s390 || grub2.name == "grub2-bls"
1✔
NEW
985
          CWM::Empty.new("console")
×
986
        else
987
          ConsoleWidget.new
1✔
988
        end
989
        VBox(
1✔
990
          VSpacing(1),
991
          MarginBox(1, 0.5, KernelAppendWidget.new),
992
          MarginBox(1, 0.5, Left(CpuMitigationsWidget.new)),
993
          MarginBox(1, 0.5, console_widget),
994
          VStretch()
995
        )
996
      end
997
    end
998

999
    # Represent tab with options related to stage1 location and bootloader type
1000
    class BootCodeTab < CWM::Tab
1✔
1001
      include Grub2Helper
1✔
1002

1003
      def label
1✔
1004
        textdomain "bootloader"
1✔
1005

1006
        _("Boot Co&de Options")
1✔
1007
      end
1008

1009
      def contents
1✔
1010
        VBox(
1✔
1011
          VSpacing(1),
1012
          HBox(
1013
            HSpacing(1),
1014
            Left(LoaderTypeWidget.new)
1015
          ),
1016
          VSpacing(1),
1017
          *widgets,
1018
          VSpacing(1),
1019
          pmbr_widget,
1020
          device_map_button,
1021
          VStretch()
1022
        )
1023
      end
1024

1025
    private
1✔
1026

1027
      def widgets
1✔
1028
        w = []
1✔
1029
        w << LoaderLocationWidget.new if loader_location_widget?
1✔
1030

1031
        if generic_mbr_widget?
1✔
1032
          w << ActivateWidget.new
1✔
1033
          w << GenericMBRWidget.new
1✔
1034
        end
1035

1036
        w << SecureBootWidget.new if secure_boot_widget?
1✔
1037
        w << TrustedBootWidget.new if trusted_boot_widget?
1✔
1038
        w << UpdateNvramWidget.new if update_nvram_widget?
1✔
1039

1040
        w.map do |widget|
1✔
1041
          MarginBox(horizontal_margin, 0, Left(widget))
3✔
1042
        end
1043
      end
1044

1045
      def pmbr_widget
1✔
1046
        return Empty() unless pmbr_widget?
1✔
1047

1048
        MarginBox(1, 0, Left(PMBRWidget.new))
1✔
1049
      end
1050

1051
      def device_map_button
1✔
1052
        return Empty() unless device_map_button?
1✔
1053

1054
        MarginBox(1, 0, Left(DeviceMapWidget.new))
1✔
1055
      end
1056

1057
      def horizontal_margin
1✔
1058
        @horizontal_margin ||= Yast::UI.TextMode ? 1 : 1.5
3✔
1059
      end
1060

1061
      def loader_location_widget?
1✔
1062
        (Yast::Arch.x86_64 || Yast::Arch.i386 || Yast::Arch.ppc) && grub2.name == "grub2"
1✔
1063
      end
1064

1065
      def generic_mbr_widget?
1✔
1066
        (Yast::Arch.x86_64 || Yast::Arch.i386) && !["grub2-efi", "grub2-bls"].include?(grub2.name)
1✔
1067
      end
1068

1069
      def secure_boot_widget?
1✔
1070
        Systeminfo.secure_boot_available?(grub2.name) && grub2.name != "grub2-bls"
1✔
1071
      end
1072

1073
      def trusted_boot_widget?
1✔
1074
        Systeminfo.trusted_boot_available?(grub2.name) && grub2.name != "grub2-bls"
1✔
1075
      end
1076

1077
      def update_nvram_widget?
1✔
1078
        Systeminfo.nvram_available?(grub2.name) && grub2.name != "grub2-bls"
1✔
1079
      end
1080

1081
      def pmbr_widget?
1✔
1082
        (Yast::Arch.x86_64 || Yast::Arch.i386) &&
1✔
1083
          Yast::BootStorage.gpt_boot_disk? &&
1084
          grub2.name != "grub2-bls"
1085
      end
1086

1087
      def device_map_button?
1✔
1088
        (Yast::Arch.x86_64 || Yast::Arch.i386) && !["grub2-efi", "grub2-bls"].include?(grub2.name)
1✔
1089
      end
1090
    end
1091

1092
    # Represents bootloader specific options like its timeout,
1093
    # default section or password protection
1094
    class BootloaderTab < CWM::Tab
1✔
1095
      include Grub2Helper
1✔
1096

1097
      def label
1✔
1098
        textdomain "bootloader"
1✔
1099

1100
        _("Boot&loader Options")
1✔
1101
      end
1102

1103
      def contents
1✔
1104
        hidden_menu_widget = if grub2.name == "grub2-bls"
1✔
NEW
1105
          CWM::Empty.new("hidden_menu")
×
1106
        else
1107
          HiddenMenuWidget.new
1✔
1108
        end
1109
        VBox(
1✔
1110
          VSpacing(2),
1111
          HBox(
1112
            HSpacing(1),
1113
            TimeoutWidget.new(hidden_menu_widget),
1114
            HSpacing(1),
1115
            VBox(
1116
              os_prober_widget,
1117
              VSpacing(1),
1118
              Left(hidden_menu_widget)
1119
            ),
1120
            HSpacing(1)
1121
          ),
1122
          VSpacing(1),
1123
          MarginBox(1, 1, MinWidth(1, DefaultSectionWidget.new)),
1124
          MarginBox(1, 1, grub_password_widget),
1125
          VStretch()
1126
        )
1127
      end
1128

1129
    private
1✔
1130

1131
      def grub_password_widget
1✔
1132
        if grub2.name == "grub2-bls"
1✔
1133
          CWM::Empty.new("password_widget")
×
1134
        else
1135
          GrubPasswordWidget.new
1✔
1136
        end
1137
      end
1138

1139
      def os_prober_widget
1✔
1140
        if OsProber.available? && # Checks !Arch.s390 and if package is available
1✔
1141
            grub2.name != "grub2-bls"
1142
          Left(OSProberWidget.new)
1✔
1143
        else
1144
          CWM::Empty.new("os_prober")
×
1145
        end
1146
      end
1147
    end
1148
  end
1149
end
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc