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

yast / yast-bootloader / 5402801408

pending completion
5402801408

push

github

schubi2
adapted suggestions

38 of 38 new or added lines in 4 files covered. (100.0%)

3188 of 3643 relevant lines covered (87.51%)

12.96 hits per line

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

68.7
/src/lib/bootloader/systemdboot.rb
1
# frozen_string_literal: true
2

3
require "fileutils"
1✔
4
require "yast"
1✔
5
require "bootloader/sysconfig"
1✔
6
require "bootloader/cpu_mitigations"
1✔
7
require "cfa/systemd_boot"
1✔
8

9
Yast.import "Report"
1✔
10
Yast.import "Arch"
1✔
11
Yast.import "ProductFeatures"
1✔
12
Yast.import "BootStorage"
1✔
13
Yast.import "Stage"
1✔
14

15
module Bootloader
1✔
16
  # Represents systemd bootloader with efi target
17
  # rubocop:disable Metrics/ClassLength
18
  class SystemdBoot < BootloaderBase
1✔
19
    include Yast::Logger
1✔
20
    include Yast::I18n
1✔
21

22
    # @!attribute menue_timeout
23
    #   @return [Integer] menue timeout
24
    attr_accessor :menue_timeout
1✔
25

26
    # @!attribute secure_boot
27
    #   @return [Boolean] current secure boot setting
28
    attr_accessor :secure_boot
1✔
29

30
    def initialize
1✔
31
      super
39✔
32

33
      textdomain "bootloader"
39✔
34
    end
35

36
    def merge(other)
1✔
37
      log.info "merging with system: timeout=#{other.menue_timeout} " \
1✔
38
               "secure_boot=#{other.secure_boot}"
39
      super
1✔
40
      self.menue_timeout = other.menue_timeout unless other.menue_timeout.nil?
1✔
41
      self.secure_boot = other.secure_boot unless other.secure_boot.nil?
1✔
42
    end
43

44
    def read
1✔
45
      super
1✔
46

47
      read_menue_timeout
1✔
48
      self.secure_boot = Systeminfo.secure_boot_active?
1✔
49
    end
50

51
    # Write bootloader settings to disk
52
    def write(etc_only: false)
1✔
53
      # super have to called as first as grub install require some config written in ancestor
54
      super
1✔
55
      log.info("Writing settings...")
1✔
56

57
      install_bootloader
1✔
58
      create_menue_entries
1✔
59
      write_menue_timeout
1✔
60

61
      true
1✔
62
    end
63

64
    def propose
1✔
65
      super
×
66
      log.info("Propose settings...")
×
67
      self.menue_timeout = Yast::ProductFeatures.GetIntegerFeature("globals", "boot_timeout").to_i
×
68
      self.secure_boot = Systeminfo.secure_boot_supported?
×
69
    end
70

71
    def status_string(status)
1✔
72
      if status
2✔
73
        _("enabled")
×
74
      else
75
        _("disabled")
2✔
76
      end
77
    end
78

79
    # Secure boot setting shown in summary screen.
80
    #
81
    # @return [String]
82
    def secure_boot_summary
1✔
83
      link = if secure_boot
2✔
84
        "<a href=\"disable_secure_boot\">(#{_("disable")})</a>"
×
85
      else
86
        "<a href=\"enable_secure_boot\">(#{_("enable")})</a>"
2✔
87
      end
88

89
      "#{_("Secure Boot:")} #{status_string(secure_boot)} #{link}"
2✔
90
    end
91

92
    # Display bootloader summary
93
    # @return a list of summary lines
94
    def summary(*)
1✔
95
      result = [
96
        Yast::Builtins.sformat(
2✔
97
          _("Boot Loader Type: %1"),
98
          "Systemd Boot"
99
        )
100
      ]
101
      result << secure_boot_summary if Systeminfo.secure_boot_available?(name)
2✔
102
      result
2✔
103
    end
104

105
    def name
1✔
106
      "systemd-boot"
12✔
107
    end
108

109
    def packages
1✔
110
      res = super
2✔
111

112
      case Yast::Arch.architecture
2✔
113
      when "x86_64"
114
        res << "shim" << "mokutil" if secure_boot
2✔
115
      else
116
        log.warn "Unknown architecture #{Yast::Arch.architecture} for EFI"
×
117
      end
118

119
      res
2✔
120
    end
121

122
    def delete
1✔
123
      delete_bootloader
12✔
124
    end
125

126
    # overwrite BootloaderBase version to save secure boot
127
    def write_sysconfig(prewrite: false)
1✔
128
      sysconfig = Bootloader::Sysconfig.new(bootloader: name,
×
129
        secure_boot: secure_boot, trusted_boot: false,
130
        update_nvram: false)
131
      prewrite ? sysconfig.pre_write : sysconfig.write
×
132
    end
133

134
  private
1✔
135

136
    LS = "/bin/ls"
1✔
137
    KERNELINSTALL = "/usr/bin/kernel-install"
1✔
138
    BOOTCTL = "/bin/bootctl"
1✔
139
    CAT = "/bin/cat"
1✔
140
    MOKUTIL = "/bin/mokutil"
1✔
141

142
    def create_menue_entries
1✔
143
      cmdline_file = File.join(Yast::Installation.destdir, "/etc/kernel/cmdline")
1✔
144
      if Yast::Stage.initial
1✔
145
        # kernel-install script needs the "root=<device>" entry in kernel parameters.
146
        # This will be written to /etc/kernel/cmdline which will be used in an
147
        # installed system by the administrator only. So we can use it because
148
        # the system will be installed new. This file will be deleted after
149
        # calling kernel-install.
150
        File.open(cmdline_file, "w+") do |fw|
×
151
          fw.puts("root=#{Yast::BootStorage.root_partitions.first.name}")
×
152
        end
153
      end
154

155
      Dir.foreach(File.join(Yast::Installation.destdir, "/usr/lib/modules")) do |kernel_name|
1✔
156
        next if [".", ".."].include?(kernel_name)
3✔
157

158
        kernel_file = File.join("/usr/lib/modules", kernel_name, "vmlinuz")
1✔
159
        if File.exist?(File.join(Yast::Installation.destdir, kernel_file))
1✔
160
          begin
161
            Yast::Execute.on_target!(KERNELINSTALL, "add",
1✔
162
              File.basename(kernel_name), kernel_file)
163
          rescue Cheetah::ExecutionFailed => e
164
            Yast::Report.Error(
×
165
            format(_(
166
                     "Cannot create systemd-boot menue entry:\n" \
167
                     "Command `%{command}`.\n" \
168
                     "Error output: %{stderr}"
169
                   ), command: e.commands.inspect, stderr: e.stderr)
170
          )
171
            break
×
172
          end
173
        end
174
      end
175
      File.delete(cmdline_file) if Yast::Stage.initial # see above
1✔
176
    end
177

178
    def read_menue_timeout
1✔
179
      config = CFA::SystemdBoot.load
1✔
180
      self.menue_timeout = config.menue_timeout.to_i if config.menue_timeout
1✔
181
    end
182

183
    def write_menue_timeout
1✔
184
      config = CFA::SystemdBoot.load
1✔
185
      config.menue_timeout = self.menue_timeout.to_s
1✔
186
      config.save
1✔
187
    end
188

189
    def bootloader_is_installed
1✔
190
      Yast::Execute.on_target(BOOTCTL, "is-installed", allowed_exitstatus: 1) == 0
13✔
191
    end
192

193
    def remove_secure_boot_settings
1✔
194
      del_files = ["/boot/efi/EFI/systemd/grub.efi",
×
195
                   "/boot/efi/EFI/systemd/systemd-bootx64.efi",
196
                   "/boot/efi/EFI/systemd/MokManager.efi"]
197
      del_files.each do |f|
×
198
        filename = File.join(Yast::Installation.destdir, f)
×
199
        File.delete(filename) if File.exist?(filename)
×
200
      end
201
    end
202

203
    def secure_boot_available
1✔
204
      ret = false
×
205
      begin
206
        ret = Yast::Execute.on_target(MOKUTIL, "--sb-state", allowed_exitstatus: 1) == 0
×
207
      rescue Cheetah::ExecutionFailed => e
208
        log.info("Command `#{e.commands.inspect}`.\n" \
×
209
                 "Error output: #{e.stderr}")
210
      end
211
      ret
×
212
    end
213

214
    def delete_bootloader
1✔
215
      return unless bootloader_is_installed
13✔
216

217
      log.info("Removing already installed systemd bootmanager.")
×
218
      begin
219
        Yast::Execute.on_target!(BOOTCTL, "remove")
×
220
      rescue Cheetah::ExecutionFailed => e
221
        Yast::Report.Error(
×
222
        format(_(
223
               "Cannot remove systemd bootloader:\n" \
224
               "Command `%{command}`.\n" \
225
               "Error output: %{stderr}"
226
             ), command: e.commands.inspect, stderr: e.stderr)
227
      )
228
        return
×
229
      end
230
      remove_secure_boot_settings
×
231
    end
232

233
    def set_secure_boot
1✔
234
      # If secure boot is enabled, shim needs to be installed.
235
      # As shim only reads grub.efi, systemd-boot needs to be renamed to pretend it's grub:
236
      log.info("Enabling secure boot options")
×
237
      src = File.join(Yast::Installation.destdir, "/boot/efi/EFI/systemd/systemd-bootx64.efi")
×
238
      dest = File.join(Yast::Installation.destdir, "/boot/efi/EFI/systemd/grub.efi")
×
239
      FileUtils.mv(src, dest) if File.exist?(src)
×
240
      src = File.join(Yast::Installation.destdir, "/usr/share/efi/", Yast::Arch.architecture,
×
241
        "/shim.efi")
242
      dest = File.join(Yast::Installation.destdir, "/boot/efi/EFI/systemd/systemd-bootx64.efi")
×
243
      FileUtils.cp(src, dest) if File.exist?(src)
×
244
      src = File.join(Yast::Installation.destdir, "/usr/share/efi/", Yast::Arch.architecture,
×
245
        "/MokManager.efi")
246
      dest = File.join(Yast::Installation.destdir, "/boot/efi/EFI/systemd/MokManager.efi")
×
247
      FileUtils.cp(src, dest) if File.exist?(src)
×
248
    end
249

250
    def install_bootloader
1✔
251
      begin
252
        delete_bootloader # if bootloader is already installed
1✔
253
        Yast::Execute.on_target!(BOOTCTL, "--make-entry-directory=yes",
1✔
254
          "install")
255
      rescue Cheetah::ExecutionFailed => e
256
        Yast::Report.Error(
×
257
        format(_(
258
                 "Cannot install systemd bootloader:\n" \
259
                 "Command `%{command}`.\n" \
260
                 "Error output: %{stderr}"
261
               ), command: e.commands.inspect, stderr: e.stderr)
262
      )
263
        return
×
264
      end
265
      return unless secure_boot
1✔
266

267
      if secure_boot_available
×
268
        set_secure_boot
×
269
      else
270
        Yast::Report.Error(_("Cannot activate secure boot because it is not available " \
×
271
                             "on your system."))
272
      end
273
    end
274
  end
275
  # rubocop:enable Metrics/ClassLength
276
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