Como recuperar Arch Linux después de una actualización que provoca el sistema no inicie

Escrito por el .
gnu-linux planeta-codigo software-libre
Comentarios

Instalar un sistema GNU/Linux es sencillo y está muy bien, también es importante saber como intentar recuperarlo cuando una actualización de software provoca que el sistema no se inicie con normalidad hasta el entorno de escritorio gráfico, el motivo puede ser incluso un kernel panic. El objetivo de la recuperación es corregir el problema del inicio, si no es posible, recuperar los valiosos documentos, imágenes, vídeos u otros archivos antes de finalmente llegar al punto de reinstalar el sistema para devolverlo a un estado correcto aunque quizá perdiendo los datos que tuviese.

Arch Linux

Estás contento con Arch Linux y en gran medida despreocupado de las versiones de las aplicaciones ya que sabes que al ser una distribución rolling release, siempre tienes la última versión de todos los paquetes cada vez que haces una actualización completa del sistema. Un día realizas la periódica actualización o has visto una nueva versión de un paquete ya sea una nueva versión del kernel o mesa que pueden contener importante mejoras en el sistema gráfico que quieres aprovechar o una nueva versión de una aplicación con nuevas características relevantes como podría se LibreOffice o VLC. Inicias la actualización del sistema como tantas otras veces has hecho. Termina aparentemente bien con todos los paquetes actualizados y sin ningún mensaje de error. Reinicias el equipo para que la actualización de algunos paquetes tengan efecto como el caso del kernel. Sin embargo, ¡sorpresa! el sistema no inicia como normalmente, no llega al inicio de sesión del entorno de escritorio ni siquiera tienes acceso a una terminal basada en consola, el error parece un kernel panic. Reinicias una segunda vez, el mismo error. Parece que está todo perdido, piensas en los importantes archivos que tienes en el sistema de almacenamiento persistente, disco duro o SSD, ¡no tienes una copia completamente reciente!, el corazón te empieza a latir con fuerza.

Por suerte, habiendo estado usando Arch Linux de forma ininterrumpida durante los 8 años esto solo me ha pasado una vez y por culpa mía después de actualizar Grub al ejecutar un comando erróneo, durante todos esos años he tenido importantes actualizaciones como varias versiones de GNOME, systemd y el kernel. El caso era el que he descrito anteriormente, el sistema no iniciaba dado que Grub es de las primeras cosas necesarias para iniciar el sistema cualquier problema en él origina esta situación. Otros paquetes de especial importancia son el kernel, los controladores gráficos, mesa, systemd, una nueva versión del entorno de escritorio, … Por una actualización a una nueva versión de VLC el sistema no va a dejar de iniciarse al lo sumo será VLC el que no inicie y es de más sencillo de solucionar volviendo a una versión anterior correcta con un simple downgrade de paquete.

Antes de una actualización conviene revisar los paquetes que se van a actualizar y fijarse en los especialmente sensibles como he comentado, también conviene estar suscrito al feed de noticias de Arch, a veces la actualización del sistema requiere una intervención manual y en ese feed siempre comentan los detalles y en qué consiste la intervención manual incluso ponen los comandos necesarios a ejecutar. Para que el daño sea el menor posible en caso de completo desastre es imprescindible hacer copias de seguridad de forma regular, una cosa es un fallo de software otra es un fallo de hardware que si se produce en la unidad de almacenamiento los datos es muy posible que queden irrecuperables. Los fallos en el hardware no son tan extraños, el hardware es reemplazable por un módico precio pero los datos no, por todo ¡haz copias de seguridad regularmente! Avisado estás.

De modo que saber cómo recuperar el sistema es algo necesario llegado el caso. En Arch Linux la forma que conozco es usando una imagen de instalación de Arch Linux y montar el sistema de archivos para hacer las modificaciones necesarias para recuperar el sistema que es probable que consistan en desactualizar paquetes por versiones anteriores. Dependiendo de los mensajes de error que proporcione el sistema en el inicio fallido las acciones a realizar para arreglarlo serán unas u otras, seguramente en los foros de Arch Linux otros usuarios ya hayan pedido ayuda con el mismo o parecido error.

Los comandos concretos para montar el sistema de archivos depende de la configuración propia: de las particiones creadas, de si se está utilizando LVM o de si se está usando cifrado. Cuando escribí el script de instalación desatendido, automatizado y personalizable para Arch Linux pensé al mismo tiempo en el caso de uso de una hipotética recuperación, escribí un script para iniciar la recuperación.

Una vez iniciado el sistema con la imagen ISO de instalación de Arch Linux este script contiene los comandos para iniciar la recuperación, los pasos de su uso son los siguientes:

1
2
3
4
5
# loadkeys es
# curl -sL https://bit.ly/2F3CATp
# vim alis-recovery.conf
# ./alis-recovery.sh
# arch-chroot /mnt

En el paso vim alis-recovery.conf se modifica la configuración según el sistema a recuperar. Básicamente se dice en qué unidad está el sistema de archivos de Arch Linux, si utiliza LVM y está cifrado. El script de recuperación está adaptado al propio script de instalación, no cubre la infinidad de casos personalizados que cada usuario por su cuenta puede realizar o existir pero sirve en cualquier caso como guía de cómo iniciar la recuperación.

El contenido completo del propio script de recuperación es el siguiente, no es más que un script de bash con los mismos comandos que serían necesarios para realizar la recuperación manualmente de forma interactiva.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
#!/usr/bin/env bash
set -e

# Arch Linux Install Script Recovery (alis-recovery) start a recovery for an
# failed installation or broken system.
# Copyright (C) 2018 picodotdev

# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

# This script is hosted at https://github.com/picodotdev/alis. For new features,
# improvements and bugs fill an issue in GitHub or make a pull request.
# Pull Request are welcome!
#
# If you test it in real hardware please send me an email to pico.dev@gmail.com with
# the machine description and tell me if somethig goes wrong or all works fine.
#
# Please, don't ask for support for this script in Arch Linux forums, first read
# the Arch Linux wiki [1], the Installation Guide [2] and the General
# Recomendations [3], later compare the commands with those of this script.
#
# [1] https://wiki.archlinux.org
# [2] https://wiki.archlinux.org/index.php/Installation_guide
# [3] https://wiki.archlinux.org/index.php/General_recommendations

# Reference:
# * [Change root](https://wiki.archlinux.org/index.php/Change_root)
# * [Deactivate volume group](https://wiki.archlinux.org/index.php/LVM#Deactivate_volume_group)

# Usage:
# # loadkeys es
# # curl https://raw.githubusercontent.com/picodotdev/alis/master/download.sh | bash, or with URL shortener curl -sL https://bit.ly/2F3CATp | bash
# # vim alis-recovery.conf
# # ./alis-recovery.sh

# global variables (no configuration, don't edit)
ASCIINEMA=""
BIOS_TYPE=""
PARTITION_BIOS=""
PARTITION_BOOT=""
PARTITION_ROOT=""
DEVICE_ROOT=""
LVM_VOLUME_PHISICAL="lvm"
LVM_VOLUME_GROUP="vg"
LVM_VOLUME_LOGICAL="root"
BOOT_DIRECTORY=""
ESP_DIRECTORY=""
#PARTITION_BOOT_NUMBER=0
UUID_BOOT=""
UUID_ROOT=""
PARTUUID_BOOT=""
PARTUUID_ROOT=""
DEVICE_SATA=""
DEVICE_NVME=""
CPU_INTEL=""
VIRTUALBOX=""
CMDLINE_LINUX_ROOT=""
CMDLINE_LINUX=""
ADDITIONAL_USER_NAMES_ARRAY=()
ADDITIONAL_USER_PASSWORDS_ARRAY=()

RED='\033[0;31m'
GREEN='\033[0;32m'
LIGHT_BLUE='\033[1;34m'
NC='\033[0m'

function configuration_install() {
    source alis-recovery.conf
}

function sanitize_variables() {
    DEVICE=$(sanitize_variable "$DEVICE")
}

function sanitize_variable() {
    VARIABLE=$1
    VARIABLE=$(echo $VARIABLE | sed "s/![^ ]*//g") # remove disabled
    VARIABLE=$(echo $VARIABLE | sed "s/ {2,}/ /g") # remove unnecessary white spaces
    VARIABLE=$(echo $VARIABLE | sed 's/^[[:space:]]*//') # trim leading
    VARIABLE=$(echo $VARIABLE | sed 's/[[:space:]]*$//') # trim trailing
    echo "$VARIABLE"
}

function check_variables() {
    check_variables_value "KEYS" "$KEYS"
    check_variables_value "DEVICE" "$DEVICE"
    check_variables_boolean "LVM" "$LVM"
    check_variables_equals "PARTITION_ROOT_ENCRYPTION_PASSWORD" "PARTITION_ROOT_ENCRYPTION_PASSWORD_RETYPE" "$PARTITION_ROOT_ENCRYPTION_PASSWORD" "$PARTITION_ROOT_ENCRYPTION_PASSWORD_RETYPE"
    check_variables_value "PING_HOSTNAME" "$PING_HOSTNAME"
}

function check_variables_value() {
    NAME=$1
    VALUE=$2
    if [ -z "$VALUE" ]; then
        echo "$NAME environment variable must have a value."
        exit
    fi
}

function check_variables_boolean() {
    NAME=$1
    VALUE=$2
    check_variables_list "$NAME" "$VALUE" "true false"
}

function check_variables_list() {
    NAME=$1
    VALUE=$2
    VALUES=$3
    REQUIRED=$4
    if [ "$REQUIRED" == "" -o "$REQUIRED" == "true" ]; then
        check_variables_value "$NAME" "$VALUE"
    fi

    if [ "$VALUE" != "" -a -z "$(echo "$VALUES" | grep -F -w "$VALUE")" ]; then
        echo "$NAME environment variable value [$VALUE] must be in [$VALUES]."
        exit
    fi
}

function check_variables_equals() {
    NAME1=$1
    NAME2=$2
    VALUE1=$3
    VALUE2=$4
    if [ "$VALUE1" != "$VALUE2" ]; then
        echo "$NAME1 and $NAME2 must be equal [$VALUE1, $VALUE2]."
        exit
    fi
}

function check_variables_size() {
    NAME=$1
    SIZE_EXPECT=$2
    SIZE=$3
    if [ "$SIZE_EXPECT" != "$SIZE" ]; then
        echo "$NAME array size [$SIZE] must be [$SIZE_EXPECT]."
        exit
    fi
}

function warning() {
    echo -e "${LIGHT_BLUE}Welcome to Arch Linux Install Script Recovery${NC}"
    echo ""
    echo "Once finalized recovery tasks execute following commands: exit, umount -R /mnt, reboot."
    echo ""
    read -p "Do you want to continue? [y/N] " yn
    case $yn in
        [Yy]* )
            ;;
        [Nn]* )
            exit
            ;;
        * )
            exit
            ;;
    esac
}

function init() {
    init_log
    loadkeys $KEYS
}

function init_log() {
    set -o xtrace
}

function facts() {
    if [ -d /sys/firmware/efi ]; then
        BIOS_TYPE="uefi"
    else
        BIOS_TYPE="bios"
    fi

    DEVICE_SATA="false"
    DEVICE_NVME="false"
    if [ -n "$(echo $DEVICE | grep "^/dev/sda")" ]; then
        DEVICE_SATA="true"
    elif [ -n "$(echo $DEVICE | grep "^/dev/nvme")" ]; then
        DEVICE_NVME="true"
    fi

    if [ -n "$(lscpu | grep GenuineIntel)" ]; then
        CPU_INTEL="true"
    fi

    if [ -n "$(lspci | grep -i virtualbox)" ]; then
        VIRTUALBOX="true"
    fi
}

function prepare() {
    prepare_partition
}

function prepare_partition() {
    if [ -d /mnt/boot ]; then
        umount /mnt/boot
        umount /mnt
    fi
    if [ -e "/dev/mapper/$LVM_VOLUME_LOGICAL" ]; then
        if [ -n "$PARTITION_ROOT_ENCRYPTION_PASSWORD" ]; then
            cryptsetup close $LVM_VOLUME_LOGICAL
        fi
    fi
    if [ -e "/dev/mapper/$LVM_VOLUME_PHISICAL" ]; then
        if [ -n "$PARTITION_ROOT_ENCRYPTION_PASSWORD" ]; then
            cryptsetup close $LVM_VOLUME_PHISICAL
        fi
    fi
}

function configure_network() {
    if [ -n "$WIFI_INTERFACE" ]; then
        cp /etc/netctl/examples/wireless-wpa /etc/netctl
        chmod 600 /etc/netctl

        sed -i 's/^Interface=.*/Interface='"$WIFI_INTERFACE"'/' /etc/netctl
        sed -i 's/^ESSID=.*/ESSID='"$WIFI_ESSID"'/' /etc/netctl
        sed -i 's/^Key=.*/Key='\''$WIFI_KEY'\''/' /etc/netctl
        if [ "$WIFI_HIDDEN" == "true" ]; then
            sed -i 's/^#Hidden=.*/Hidden=yes/' /etc/netctl
        fi

        netctl start wireless-wpa
    fi

    ping -c 5 $PING_HOSTNAME
    if [ $? -ne 0 ]; then
        echo "Network ping check failed."
    fi
}

function partition() {
    if [ "$BIOS_TYPE" == "uefi" ]; then
        if [ "$DEVICE_SATA" == "true" ]; then
            PARTITION_BOOT="${DEVICE}1"
            PARTITION_ROOT="${DEVICE}2"
            #PARTITION_BOOT_NUMBER=1
            DEVICE_ROOT="${DEVICE}2"
        fi
        
        if [ "$DEVICE_NVME" == "true" ]; then
            PARTITION_BOOT="${DEVICE}p1"
            PARTITION_ROOT="${DEVICE}p2"
            #PARTITION_BOOT_NUMBER=1
            DEVICE_ROOT="${DEVICE}p2"
        fi
    fi

    if [ "$BIOS_TYPE" == "bios" ]; then
        if [ "$DEVICE_SATA" == "true" ]; then
            PARTITION_BIOS="${DEVICE}1"
            PARTITION_BOOT="${DEVICE}2"
            PARTITION_ROOT="${DEVICE}3"
            #PARTITION_BOOT_NUMBER=2
            DEVICE_ROOT="${DEVICE}3"
        fi
        
        if [ "$DEVICE_NVME" == "true" ]; then
            PARTITION_BIOS="${DEVICE}p1"
            PARTITION_BOOT="${DEVICE}p2"
            PARTITION_ROOT="${DEVICE}p3"
            #PARTITION_BOOT_NUMBER=2
            DEVICE_ROOT="${DEVICE}p3"
        fi
    fi

    if [ -n "$PARTITION_ROOT_ENCRYPTION_PASSWORD" ]; then
        echo -n "$PARTITION_ROOT_ENCRYPTION_PASSWORD" | cryptsetup --key-file=- open $PARTITION_ROOT $LVM_VOLUME_PHISICAL
        sleep 5
    fi

    if [ "$LVM" == "true" ]; then
        DEVICE_ROOT_MAPPER="$LVM_VOLUME_GROUP-$LVM_VOLUME_LOGICAL"
        DEVICE_ROOT="/dev/mapper/$DEVICE_ROOT_MAPPER"
    fi

    PARTITION_OPTIONS=""

    if [ "$DEVICE_TRIM" == "true" ]; then
        PARTITION_OPTIONS="defaults,noatime"
    fi

    mount -o "$PARTITION_OPTIONS" $DEVICE_ROOT /mnt
    mount -o "$PARTITION_OPTIONS" $PARTITION_BOOT /mnt/boot
}

function recovery() {
    arch-chroot /mnt
}

function main() {
    configuration_install
    sanitize_variables
    check_variables
    warning
    init
    facts
    prepare
    partition
    #recovery
}

main

El código fuente completo del ejemplo puedes descargarlo del repositorio de ejemplos de Blog Bitix alojado en GitHub.