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

yast / yast-bootloader / 5057248293

pending completion
5057248293

push

github

Unknown Committer
Unknown Commit Message

30 of 30 new or added lines in 7 files covered. (100.0%)

3186 of 3633 relevant lines covered (87.7%)

12.99 hits per line

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

73.03
/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

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

14
module Bootloader
1✔
15
  # Represents systemd bootloader with efi target
16
  class SystemdBoot < BootloaderBase
1✔
17
    include Yast::Logger
1✔
18
    include Yast::I18n
1✔
19

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

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

28
    def initialize
1✔
29
      super
39✔
30

31
      textdomain "bootloader"
39✔
32
      self.menue_timeout = Yast::ProductFeatures.GetIntegerFeature("globals", "boot_timeout").to_i
39✔
33
    end
34

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

42
    def cpu_mitigations
1✔
43
      Bootloader::CpuMitigations.new(:manual)
×
44
    end
45

46
    def cpu_mitigations=(value)
1✔
47
      log.info "ignoring set of cpu_mitigations (#{value}) in systemd bootloader."
×
48
    end
49

50
    def read
1✔
51
      super
1✔
52

53
      read_menue_timeout
1✔
54
      self.secure_boot = Systeminfo.secure_boot_active?
1✔
55
    end
56

57
    # Write bootloader settings to disk
58
    def write(etc_only: false)
1✔
59
      # super have to called as first as grub install require some config written in ancestor
60
      super
1✔
61
      log.info("Writing settings...")
1✔
62

63
      install_bootloader
1✔
64
      create_menue_entries
1✔
65
      write_menue_timeout
1✔
66

67
      true
1✔
68
    end
69

70
    def propose
1✔
71
      super
×
72
      log.info("Propose settings...")
×
73
    end
74

75
    def status_string(status)
1✔
76
      if status
2✔
77
        _("enabled")
×
78
      else
79
        _("disabled")
2✔
80
      end
81
    end
82

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

93
      "#{_("Secure Boot:")} #{status_string(secure_boot)} #{link}"
2✔
94
    end
95

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

109
    def name
1✔
110
      "systemd-boot"
15✔
111
    end
112

113
    def packages
1✔
114
      res = super
2✔
115

116
      case Yast::Arch.architecture
2✔
117
      when "x86_64"
118
        res << "shim" << "mokutil" if secure_boot
2✔
119
      when "aarch64"
120
        res << "shim" << "mokutil" if secure_boot
×
121
      else
122
        log.warn "Unknown architecture #{Yast::Arch.architecture} for EFI"
×
123
      end
124

125
      res
2✔
126
    end
127

128
    def delete
1✔
129
      delete_bootloader
11✔
130
    end
131

132
    # overwrite BootloaderBase version to save secure boot
133
    def write_sysconfig(prewrite: false)
1✔
134
      sysconfig = Bootloader::Sysconfig.new(bootloader: name,
×
135
        secure_boot: secure_boot, trusted_boot: false,
136
        update_nvram: false)
137
      prewrite ? sysconfig.pre_write : sysconfig.write
×
138
    end
139

140
  private
1✔
141

142
    LS = "/bin/ls"
1✔
143
    KERNELINSTALL = "/usr/bin/kernel-install"
1✔
144
    BOOTCTL = "/bin/bootctl"
1✔
145
    CAT = "/bin/cat"
1✔
146
    LOADERCONF = "/boot/efi/loader/loader.conf"
1✔
147
    MOKUTIL = "/bin/mokutil"
1✔
148

149
    def create_menue_entries
1✔
150
      cmdline_file = File.join(Yast::Installation.destdir, "/etc/kernel/cmdline")
1✔
151
      if Yast::Stage.initial
1✔
152
        # kernel-install script needs the "root=<device>" entry in kernel parameters.
153
        File.open(cmdline_file, "w+") do |fw|
×
154
          fw.puts("root=#{Yast::BootStorage.root_partitions.first.name}")
×
155
        end
156
      end
157

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

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

181
    def read_menue_timeout
1✔
182
      filename = File.join(Yast::Installation.destdir, LOADERCONF)
1✔
183
      if File.file?(filename)
1✔
184
        f = File.open(filename, "r")
1✔
185
        f.each_line do |line|
1✔
186
          line.lstrip!
1✔
187
          next if line.start_with?("#")
1✔
188

189
          if line.start_with?("timeout")
1✔
190
            self.menue_timeout = line.split(" ")[1].to_i
1✔
191
            break
1✔
192
          end
193
        end
194
      end
195
    end
196

197
    def write_menue_timeout
1✔
198
      filename = File.join(Yast::Installation.destdir, LOADERCONF)
1✔
199
      if File.file?(filename)
1✔
200
        output = []
1✔
201
        entry_set = false
1✔
202
        f = File.open(filename, "r")
1✔
203
        f.each_line do |line|
1✔
204
          line.lstrip!
3✔
205
          if line.start_with?("timeout") || line.start_with?("#timeout")
3✔
206
            output << "timeout #{menue_timeout}\n"
1✔
207
            entry_set = true
1✔
208
            next
1✔
209
          end
210
          output << line
2✔
211
        end
212
        output << "timeout #{menue_timeout}" unless entry_set
1✔
213
        File.open(filename, "w+") do |fw|
1✔
214
          fw.puts(output)
1✔
215
        end
216
      end
217
    end
218

219
    def bootloader_is_installed
1✔
220
      Yast::Execute.on_target(BOOTCTL, "is-installed", allowed_exitstatus: 1) == 0
12✔
221
    end
222

223
    def remove_secure_boot_settings
1✔
224
      del_files = ["/boot/efi/EFI/systemd/grub.efi",
×
225
                   "/boot/efi/EFI/systemd/systemd-bootx64.efi",
226
                   "/boot/efi/EFI/systemd/MokManager.efi"]
227
      del_files.each do |f|
×
228
        filename = File.join(Yast::Installation.destdir, f)
×
229
        File.delete(filename) if File.exist?(filename)
×
230
      end
231
    end
232

233
    def secure_boot_available
1✔
234
      ret = false
×
235
      begin
236
        ret = Yast::Execute.on_target(MOKUTIL, "--sb-state", allowed_exitstatus: 1) == 0
×
237
      rescue Cheetah::ExecutionFailed => e
238
        log.info("Command `#{e.commands.inspect}`.\n" \
×
239
                 "Error output: #{e.stderr}")
240
      end
241
      ret
×
242
    end
243

244
    def delete_bootloader
1✔
245
      if bootloader_is_installed
12✔
246
        log.info("Removing already installed systemd bootmanager.")
×
247
        begin
248
          Yast::Execute.on_target!(BOOTCTL, "remove")
×
249
        rescue Cheetah::ExecutionFailed => e
250
          Yast::Report.Error(
×
251
          format(_(
252
                 "Cannot remove systemd bootloader:\n" \
253
                 "Command `%{command}`.\n" \
254
                 "Error output: %{stderr}"
255
               ), command: e.commands.inspect, stderr: e.stderr)
256
        )
257
          return
×
258
        end
259
        remove_secure_boot_settings
×
260
      end
261
    end
262

263
    def install_bootloader
1✔
264
      begin
265
        delete_bootloader # if bootloader is already installed
1✔
266
        Yast::Execute.on_target!(BOOTCTL, "--make-entry-directory=yes",
1✔
267
          "install")
268
      rescue Cheetah::ExecutionFailed => e
269
        Yast::Report.Error(
×
270
        format(_(
271
                 "Cannot install systemd bootloader:\n" \
272
                 "Command `%{command}`.\n" \
273
                 "Error output: %{stderr}"
274
               ), command: e.commands.inspect, stderr: e.stderr)
275
      )
276
        return
×
277
      end
278

279
      if secure_boot
1✔
280
        if secure_boot_available
×
281
          log.info("Enabling secure boot options")
×
282
          src = File.join(Yast::Installation.destdir, "/boot/efi/EFI/systemd/systemd-bootx64.efi")
×
283
          dest = File.join(Yast::Installation.destdir, "/boot/efi/EFI/systemd/grub.efi")
×
284
          FileUtils.mv(src, dest) if File.exist?(src)
×
285
          src = File.join(Yast::Installation.destdir, "/usr/share/efi/", Yast::Arch.architecture,
×
286
            "/shim.efi")
287
          dest = File.join(Yast::Installation.destdir, "/boot/efi/EFI/systemd/systemd-bootx64.efi")
×
288
          FileUtils.cp(src, dest) if File.exist?(src)
×
289
          src = File.join(Yast::Installation.destdir, "/usr/share/efi/", Yast::Arch.architecture,
×
290
            "/MokManager.efi")
291
          dest = File.join(Yast::Installation.destdir, "/boot/efi/EFI/systemd/MokManager.efi")
×
292
          FileUtils.cp(src, dest) if File.exist?(src)
×
293
        else
294
          Yast::Report.Error(_("Cannot activate secure boot because it is not available on your system."))
×
295
        end
296
      end
297
    end
298
  end
299
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