source: calamares/trunk/fuentes/src/modules/bootloader/main.py @ 7538

Last change on this file since 7538 was 7538, checked in by kbut, 13 months ago

sync with github

File size: 12.6 KB
Line 
1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3#
4# === This file is part of Calamares - <https://github.com/calamares> ===
5#
6#   Copyright 2014, Aurélien Gâteau <agateau@kde.org>
7#   Copyright 2014, Anke Boersma <demm@kaosx.us>
8#   Copyright 2014, Daniel Hillenbrand <codeworkx@bbqlinux.org>
9#   Copyright 2014, Benjamin Vaudour <benjamin.vaudour@yahoo.fr>
10#   Copyright 2014, Kevin Kofler <kevin.kofler@chello.at>
11#   Copyright 2015-2017, Philip Mueller <philm@manjaro.org>
12#   Copyright 2016-2017, Teo Mrnjavac <teo@kde.org>
13#   Copyright 2017, Alf Gaida <agaida@siduction.org>
14#   Copyright 2017-2018, Adriaan de Groot <groot@kde.org>
15#   Copyright 2017, Gabriel Craciunescu <crazy@frugalware.org>
16#   Copyright 2017, Ben Green <Bezzy1999@hotmail.com>
17#
18#   Calamares is free software: you can redistribute it and/or modify
19#   it under the terms of the GNU General Public License as published by
20#   the Free Software Foundation, either version 3 of the License, or
21#   (at your option) any later version.
22#
23#   Calamares is distributed in the hope that it will be useful,
24#   but WITHOUT ANY WARRANTY; without even the implied warranty of
25#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26#   GNU General Public License for more details.
27#
28#   You should have received a copy of the GNU General Public License
29#   along with Calamares. If not, see <http://www.gnu.org/licenses/>.
30
31import os
32import shutil
33import subprocess
34
35import libcalamares
36
37from libcalamares.utils import check_target_env_call
38
39
40def get_uuid():
41    """
42    Checks and passes 'uuid' to other routine.
43
44    :return:
45    """
46    root_mount_point = libcalamares.globalstorage.value("rootMountPoint")
47    partitions = libcalamares.globalstorage.value("partitions")
48
49    for partition in partitions:
50        if partition["mountPoint"] == "/":
51            libcalamares.utils.debug("Root partition uuid: \"{!s}\"".format(partition["uuid"]))
52            return partition["uuid"]
53
54    return ""
55
56
57def get_bootloader_entry_name():
58    """
59    Passes 'bootloader_entry_name' to other routine based
60    on configuration file.
61
62    :return:
63    """
64    if "bootloaderEntryName" in libcalamares.job.configuration:
65        return libcalamares.job.configuration["bootloaderEntryName"]
66    else:
67        branding = libcalamares.globalstorage.value("branding")
68        return branding["bootloaderEntryName"]
69
70
71def get_kernel_line(kernel_type):
72    """
73    Passes 'kernel_line' to other routine based on configuration file.
74
75    :param kernel_type:
76    :return:
77    """
78    if kernel_type == "fallback":
79        if "fallbackKernelLine" in libcalamares.job.configuration:
80            return libcalamares.job.configuration["fallbackKernelLine"]
81        else:
82            return " (fallback)"
83    else:
84        if "kernelLine" in libcalamares.job.configuration:
85            return libcalamares.job.configuration["kernelLine"]
86        else:
87            return ""
88
89
90def create_systemd_boot_conf(uuid, conf_path, kernel_line):
91    """
92    Creates systemd-boot configuration files based on given parameters.
93
94    :param uuid:
95    :param conf_path:
96    :param kernel_line:
97    """
98    distribution = get_bootloader_entry_name()
99    kernel = libcalamares.job.configuration["kernel"]
100    img = libcalamares.job.configuration["img"]
101    kernel_params = ["quiet"]
102
103    partitions = libcalamares.globalstorage.value("partitions")
104    swap_uuid = ""
105
106    cryptdevice_params = []
107
108    # Take over swap settings:
109    #  - unencrypted swap partition sets swap_uuid
110    #  - encrypted root sets cryptdevice_params
111    for partition in partitions:
112        has_luks = "luksMapperName" in partition
113        if partition["fs"] == "linuxswap" and not has_luks:
114            swap_uuid = partition["uuid"]
115
116        if partition["mountPoint"] == "/" and has_luks:
117            cryptdevice_params = ["cryptdevice=UUID="
118                                  + partition["luksUuid"]
119                                  + ":"
120                                  + partition["luksMapperName"],
121                                  "root=/dev/mapper/"
122                                  + partition["luksMapperName"],
123                                  "resume=/dev/mapper/"
124                                  + partition["luksMapperName"]]
125
126    if cryptdevice_params:
127        kernel_params.extend(cryptdevice_params)
128    else:
129        kernel_params.append("root=UUID={!s}".format(uuid))
130
131    if swap_uuid:
132        kernel_params.append("resume=UUID={!s}".format(swap_uuid))
133
134    lines = [
135        '## This is just an example config file.\n',
136        '## Please edit the paths and kernel parameters according\n',
137        '## to your system.\n',
138        '\n',
139        "title   {!s}{!s}\n".format(distribution, kernel_line),
140        "linux   {!s}\n".format(kernel),
141        "initrd  {!s}\n".format(img),
142        "options {!s} rw\n".format(" ".join(kernel_params)),
143    ]
144
145    with open(conf_path, 'w') as conf_file:
146        for line in lines:
147            conf_file.write(line)
148
149
150def create_loader(loader_path):
151    """
152    Writes configuration for loader.
153
154    :param loader_path:
155    """
156    distribution = get_bootloader_entry_name()
157    timeout = libcalamares.job.configuration["timeout"]
158    file_name_sanitizer = str.maketrans(" /", "_-")
159    distribution_translated = distribution.translate(file_name_sanitizer)
160    lines = [
161        "timeout {!s}\n".format(timeout),
162        "default {!s}\n".format(distribution_translated),
163    ]
164
165    with open(loader_path, 'w') as loader_file:
166        for line in lines:
167            loader_file.write(line)
168
169
170def install_systemd_boot(efi_directory):
171    """
172    Installs systemd-boot as bootloader for EFI setups.
173
174    :param efi_directory:
175    """
176    libcalamares.utils.debug("Bootloader: systemd-boot")
177    install_path = libcalamares.globalstorage.value("rootMountPoint")
178    install_efi_directory = install_path + efi_directory
179    uuid = get_uuid()
180    distribution = get_bootloader_entry_name()
181    file_name_sanitizer = str.maketrans(" /", "_-")
182    distribution_translated = distribution.translate(file_name_sanitizer)
183    conf_path = os.path.join(install_efi_directory,
184                             "loader",
185                             "entries",
186                             distribution_translated + ".conf")
187    fallback_path = os.path.join(install_efi_directory,
188                                 "loader",
189                                 "entries",
190                                 distribution_translated + "-fallback.conf")
191    loader_path = os.path.join(install_efi_directory,
192                               "loader",
193                               "loader.conf")
194    subprocess.call(["bootctl",
195                     "--path={!s}".format(install_efi_directory),
196                     "install"])
197    kernel_line = get_kernel_line("default")
198    libcalamares.utils.debug("Configure: \"{!s}\"".format(kernel_line))
199    create_systemd_boot_conf(uuid, conf_path, kernel_line)
200    kernel_line = get_kernel_line("fallback")
201    libcalamares.utils.debug("Configure: \"{!s}\"".format(kernel_line))
202    create_systemd_boot_conf(uuid, fallback_path, kernel_line)
203    create_loader(loader_path)
204
205
206def install_grub(efi_directory, fw_type):
207    """
208    Installs grub as bootloader, either in pc or efi mode.
209
210    :param efi_directory:
211    :param fw_type:
212    """
213    if fw_type == "efi":
214        libcalamares.utils.debug("Bootloader: grub (efi)")
215        install_path = libcalamares.globalstorage.value("rootMountPoint")
216        install_efi_directory = install_path + efi_directory
217
218        if not os.path.isdir(install_efi_directory):
219            os.makedirs(install_efi_directory)
220
221        if "efiBootloaderId" in libcalamares.job.configuration:
222            efi_bootloader_id = libcalamares.job.configuration[
223                                    "efiBootloaderId"]
224        else:
225            branding = libcalamares.globalstorage.value("branding")
226            distribution = branding["bootloaderEntryName"]
227            file_name_sanitizer = str.maketrans(" /", "_-")
228            efi_bootloader_id = distribution.translate(file_name_sanitizer)
229        # get bitness of the underlying UEFI
230        try:
231            sysfile = open("/sys/firmware/efi/fw_platform_size", "r")
232            efi_bitness = sysfile.read(2)
233        except Exception:
234            # if the kernel is older than 4.0, the UEFI bitness likely isn't
235            # exposed to the userspace so we assume a 64 bit UEFI here
236            efi_bitness = "64"
237
238        if efi_bitness == "32":
239            efi_target = "i386-efi"
240            efi_grub_file = "grubia32.efi"
241            efi_boot_file = "bootia32.efi"
242        elif efi_bitness == "64":
243            efi_target = "x86_64-efi"
244            efi_grub_file = "grubx64.efi"
245            efi_boot_file = "bootx64.efi"
246
247        check_target_env_call([libcalamares.job.configuration["grubInstall"],
248                               "--target=" + efi_target,
249                               "--efi-directory=" + efi_directory,
250                               "--bootloader-id=" + efi_bootloader_id,
251                               "--force"])
252
253        # VFAT is weird, see issue CAL-385
254        install_efi_directory_firmware = (vfat_correct_case(
255                                              install_efi_directory,
256                                              "EFI"))
257        if not os.path.exists(install_efi_directory_firmware):
258            os.makedirs(install_efi_directory_firmware)
259
260        # there might be several values for the boot directory
261        # most usual they are boot, Boot, BOOT
262
263        install_efi_boot_directory = (vfat_correct_case(
264                                          install_efi_directory_firmware,
265                                          "boot"))
266        if not os.path.exists(install_efi_boot_directory):
267            os.makedirs(install_efi_boot_directory)
268
269        # Workaround for some UEFI firmwares
270        FALLBACK = "installEFIFallback"
271        libcalamares.utils.debug("UEFI Fallback: " + str(libcalamares.job.configuration.get(FALLBACK, "<unset>")))
272        if libcalamares.job.configuration.get(FALLBACK, True):
273            libcalamares.utils.debug("  .. installing '{!s}' fallback firmware".format(efi_boot_file))
274            efi_file_source = os.path.join(install_efi_directory_firmware,
275                                        efi_bootloader_id,
276                                        efi_grub_file)
277            efi_file_target = os.path.join(install_efi_boot_directory,
278                                        efi_boot_file)
279
280            shutil.copy2(efi_file_source, efi_file_target)
281    else:
282        libcalamares.utils.debug("Bootloader: grub (bios)")
283        if libcalamares.globalstorage.value("bootLoader") is None:
284            return
285
286        boot_loader = libcalamares.globalstorage.value("bootLoader")
287        if boot_loader["installPath"] is None:
288            return
289
290        check_target_env_call([libcalamares.job.configuration["grubInstall"],
291                               "--target=i386-pc",
292                               "--recheck",
293                               "--force",
294                               boot_loader["installPath"]])
295
296    # The file specified in grubCfg should already be filled out
297    # by the grubcfg job module.
298    check_target_env_call([libcalamares.job.configuration["grubMkconfig"],
299                           "-o", libcalamares.job.configuration["grubCfg"]])
300
301
302def vfat_correct_case(parent, name):
303    for candidate in os.listdir(parent):
304        if name.lower() == candidate.lower():
305            return os.path.join(parent, candidate)
306    return os.path.join(parent, name)
307
308
309def prepare_bootloader(fw_type):
310    """
311    Prepares bootloader.
312    Based on value 'efi_boot_loader', it either calls systemd-boot
313    or grub to be installed.
314
315    :param fw_type:
316    :return:
317    """
318    efi_boot_loader = libcalamares.job.configuration["efiBootLoader"]
319    efi_directory = libcalamares.globalstorage.value("efiSystemPartition")
320
321    if efi_boot_loader == "systemd-boot" and fw_type == "efi":
322        install_systemd_boot(efi_directory)
323    else:
324        install_grub(efi_directory, fw_type)
325
326
327def run():
328    """
329    Starts procedure and passes 'fw_type' to other routine.
330
331    :return:
332    """
333
334    fw_type = libcalamares.globalstorage.value("firmwareType")
335
336    if (libcalamares.globalstorage.value("bootLoader") is None
337            and fw_type != "efi"):
338        return None
339
340    partitions = libcalamares.globalstorage.value("partitions")
341
342    if fw_type == "efi":
343        esp_found = False
344
345        for partition in partitions:
346            if (partition["mountPoint"] ==
347                    libcalamares.globalstorage.value("efiSystemPartition")):
348                esp_found = True
349
350        if not esp_found:
351            return None
352
353    prepare_bootloader(fw_type)
354
355    return None
Note: See TracBrowser for help on using the repository browser.