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

yast / yast-bootloader / 12048550996

27 Nov 2024 10:37AM UTC coverage: 87.484% (+0.1%) from 87.378%
12048550996

Pull #708

github

schubi2
cleanup
Pull Request #708: Supporting Grub2-BLS

210 of 240 new or added lines in 13 files covered. (87.5%)

88 existing lines in 7 files now uncovered.

3362 of 3843 relevant lines covered (87.48%)

13.0 hits per line

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

87.95
/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 "Initrd"
1✔
15
Yast.import "Label"
1✔
16
Yast.import "Report"
1✔
17
Yast.import "UI"
1✔
18
Yast.import "Mode"
1✔
19
Yast.import "Arch"
1✔
20

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

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

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

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

41
      def grub2
1✔
42
        BootloaderFactory.current
24✔
43
      end
44
    end
45

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

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

53
        super()
11✔
54

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

60
      attr_reader :minimum, :maximum
1✔
61

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

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

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

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

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

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

99
        super
5✔
100
      end
101

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

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

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

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

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

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

133
        super
5✔
134
      end
135

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

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

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

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

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

161
      def initialize
1✔
162
        textdomain "bootloader"
5✔
163

164
        super
5✔
165
      end
166

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

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

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

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

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

189
        super
5✔
190
      end
191

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

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

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

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

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

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

219
        super
6✔
220
      end
221

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

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

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

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

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

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

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

261
        super
4✔
262
      end
263

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

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

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

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

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

UNCOV
303
        Yast::Popup.ContinueCancel(
×
304
          # text is identical like one in proposal client. Keep in sync!
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)
×
793
        Yast::UI.ChangeWidget(Id(:custom_list), :Enabled, checked)
×
794

795
        nil
796
      end
797

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

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

UNCOV
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

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

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

UNCOV
837
        invalid_devs = invalid_custom_devices(devs)
×
838
        if !invalid_devs.empty?
×
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

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

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✔
UNCOV
875
        if custom_devices.empty?
×
UNCOV
876
          Yast::UI.ChangeWidget(:custom, :Value, false)
×
UNCOV
877
          Yast::UI.ChangeWidget(:custom_list, :Enabled, false)
×
878
        else
UNCOV
879
          Yast::UI.ChangeWidget(:custom, :Value, true)
×
UNCOV
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

UNCOV
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
        VBox(
1✔
985
          VSpacing(1),
986
          MarginBox(1, 0.5, KernelAppendWidget.new),
987
          MarginBox(1, 0.5, Left(CpuMitigationsWidget.new)),
988
          MarginBox(1, 0.5, console_widget),
989
          VStretch()
990
        )
991
      end
992

993
    private
1✔
994

995
      def console_widget
1✔
996
        if Systeminfo.console_supported?(grub2.name)
1✔
997
          ConsoleWidget.new
1✔
998
        else
NEW
UNCOV
999
          CWM::Empty.new("console")
×
1000
        end
1001
      end
1002
    end
1003

1004
    # Represent tab with options related to stage1 location and bootloader type
1005
    class BootCodeTab < CWM::Tab
1✔
1006
      include Grub2Helper
1✔
1007

1008
      def label
1✔
1009
        textdomain "bootloader"
1✔
1010

1011
        _("Boot Co&de Options")
1✔
1012
      end
1013

1014
      def contents
1✔
1015
        VBox(
1✔
1016
          VSpacing(1),
1017
          HBox(
1018
            HSpacing(1),
1019
            Left(LoaderTypeWidget.new)
1020
          ),
1021
          VSpacing(1),
1022
          *widgets,
1023
          VSpacing(1),
1024
          pmbr_widget,
1025
          device_map_button,
1026
          VStretch()
1027
        )
1028
      end
1029

1030
    private
1✔
1031

1032
      def widgets
1✔
1033
        w = []
1✔
1034
        w << LoaderLocationWidget.new if loader_location_widget?
1✔
1035

1036
        if generic_mbr_widget?
1✔
1037
          w << ActivateWidget.new
1✔
1038
          w << GenericMBRWidget.new
1✔
1039
        end
1040

1041
        w << SecureBootWidget.new if secure_boot_widget?
1✔
1042
        w << TrustedBootWidget.new if trusted_boot_widget?
1✔
1043
        w << UpdateNvramWidget.new if update_nvram_widget?
1✔
1044

1045
        w.map do |widget|
1✔
1046
          MarginBox(horizontal_margin, 0, Left(widget))
3✔
1047
        end
1048
      end
1049

1050
      def pmbr_widget
1✔
1051
        return Empty() unless pmbr_widget?
1✔
1052

1053
        MarginBox(1, 0, Left(PMBRWidget.new))
1✔
1054
      end
1055

1056
      def device_map_button
1✔
1057
        return Empty() unless device_map_button?
1✔
1058

1059
        MarginBox(1, 0, Left(DeviceMapWidget.new))
1✔
1060
      end
1061

1062
      def horizontal_margin
1✔
1063
        @horizontal_margin ||= Yast::UI.TextMode ? 1 : 1.5
3✔
1064
      end
1065

1066
      def loader_location_widget?
1✔
1067
        Systeminfo.loader_location_available?(grub2.name)
1✔
1068
      end
1069

1070
      def generic_mbr_widget?
1✔
1071
        Systeminfo.generic_mbr_available?(grub2.name)
1✔
1072
      end
1073

1074
      def secure_boot_widget?
1✔
1075
        Systeminfo.secure_boot_available?(grub2.name)
1✔
1076
      end
1077

1078
      def trusted_boot_widget?
1✔
1079
        Systeminfo.trusted_boot_available?(grub2.name)
1✔
1080
      end
1081

1082
      def update_nvram_widget?
1✔
1083
        Systeminfo.nvram_available?(grub2.name)
1✔
1084
      end
1085

1086
      def pmbr_widget?
1✔
1087
        Systeminfo.pmbr_available?(grub2.name)
1✔
1088
      end
1089

1090
      def device_map_button?
1✔
1091
        Systeminfo.device_map?(grub2.name)
1✔
1092
      end
1093
    end
1094

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

1100
      def label
1✔
1101
        textdomain "bootloader"
1✔
1102

1103
        _("Boot&loader Options")
1✔
1104
      end
1105

1106
      def contents
1✔
1107
        VBox(
1✔
1108
          VSpacing(2),
1109
          HBox(
1110
            HSpacing(1),
1111
            TimeoutWidget.new(hidden_menu_widget),
1112
            HSpacing(1),
1113
            VBox(
1114
              os_prober_widget,
1115
              VSpacing(1),
1116
              Left(hidden_menu_widget)
1117
            ),
1118
            HSpacing(1)
1119
          ),
1120
          VSpacing(1),
1121
          MarginBox(1, 1, MinWidth(1, DefaultSectionWidget.new)),
1122
          MarginBox(1, 1, grub_password_widget),
1123
          VStretch()
1124
        )
1125
      end
1126

1127
    private
1✔
1128

1129
      def grub_password_widget
1✔
1130
        if Systeminfo.password_supported?(grub2.name)
1✔
1131
          GrubPasswordWidget.new
1✔
1132
        else
NEW
UNCOV
1133
          CWM::Empty.new("password_widget")
×
1134
        end
1135
      end
1136

1137
      def hidden_menu_widget
1✔
1138
        if Systeminfo.hiding_menu_supported?(grub2.name)
2✔
1139
          HiddenMenuWidget.new
2✔
1140
        else
NEW
UNCOV
1141
          CWM::Empty.new("hidden_menu")
×
1142
        end
1143
      end
1144

1145
      def os_prober_widget
1✔
1146
        # Checks !Arch.s390, not grub2-bls  and if package is available
1147
        if OsProber.available?(grub2.name)
1✔
1148
          Left(OSProberWidget.new)
1✔
1149
        else
UNCOV
1150
          CWM::Empty.new("os_prober")
×
1151
        end
1152
      end
1153
    end
1154
  end
1155
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

© 2025 Coveralls, Inc