Slackware 14.2 の LVM への OS インストール例

# [当方執筆の技術情報に関するライセンス、免責事項、禁止事項](https://qiita.com/EarthSimilarityIndex/items/6b9bb2ccacb38aae15b1)
****
# 執筆動機
lvm に関連する一連のコマンドラインツール群、及び eliloconfig を活用することで、そこそこシンプルに [LVM](https://ja.wikipedia.org/wiki/%E8%AB%96%E7%90%86%E3%83%9C%E3%83%AA%E3%83%A5%E3%83%BC%E3%83%A0%E3%83%9E%E3%83%8D%E3%83%BC%E3%82%B8%E3%83%A3) 領域への Slackware のインストールが可能でした。

※ なお、[ESP（ EFI システムパーティション）](https://ja.wikipedia.org/wiki/EFI%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E3%83%91%E3%83%BC%E3%83%86%E3%82%A3%E3%82%B7%E3%83%A7%E3%83%B3)は LVM の中には置けないようです。[おのれディケイドぉぉぉぉぉ！！](https://dic.pixiv.net/a/%E3%81%8A%E3%81%AE%E3%82%8C%E3%83%87%E3%82%A3%E3%82%B1%E3%82%A4%E3%83%89)

# 参考文献：

<details><summary>参考文献：</summary><div>

https://www.slackwiki.com/LVM/Luks_Encryption
https://wa3.i-3-i.info/word25557.html
https://wa3.i-3-i.info/word12796.html
https://www.debian.org/releases/stable/i386/apcs04.ja.html
https://wiki.archlinux.jp/index.php/LVM
https://wiki.gentoo.org/wiki/LVM/ja
https://ansaikuropedia.org/wiki/Uncyclopedia:%E9%A6%AC%E9%B9%BF%E3%81%AB%E3%81%AA%E3%82%89%E3%81%9A%E3%81%AB%E3%83%90%E3%82%AB%E3%83%90%E3%82%AB%E3%81%97%E3%81%8F%E3%82%84%E3%82%8B%E6%96%B9%E6%B3%95
https://www.linuxquestions.org/questions/slackware-14/slackware-current-elilo-lvm-4175534140/
http://www.slackware.com/~alien/archive/13.1/README_LVM.TXT
https://docs.slackware.com/howtos:slackware_admin:set_up_syslinux_as_boot_loader_on_uefi_based_hardware
http://docs.slackware.com/howtos:slackware_admin:set_up_grub_as_boot_loader_on_uefi_based_hardware
https://docs.slackware.com/howtos:slackware_admin:installing_on_uefi_hardware
https://www.reddit.com/r/slackware/comments/4ydl83/slackware_and_uefi_help/
https://blog.paranoidpenguin.net/2016/07/configuring-elilo-with-a-generic-kernel-on-slackware-14-2/
https://www.linuxquestions.org/questions/slackware-14/slackpkg-lilo-elilo-initrd-4175628620/
https://www.ibm.com/developerworks/jp/linux/library/l-initrd/index.html
https://tech.nikkeibp.co.jp/it/article/COLUMN/20061025/251702/
https://linux.die.net/man/8/mkinitrd
https://duckduckgo.com/?q=lvm+can't+deactivate
https://duckduckgo.com/?q=LVM+アンマウント
https://rin-ka.net/centos7-lvm/
https://www.youtube.com/watch?v=njxzwMGTWWs
https://wiki.gentoo.org/wiki/Chroot/ja
https://wiki.archlinux.jp/index.php/Chroot
http://toaruhenjin.com/digimono-review/hotswap
https://www.linuxquestions.org/linux/answers/Hardware/Mini_HOW_TO_Hibernate_and_resume_0
https://wiki.gentoo.org/wiki/Suspend_and_hibernate
https://takuya-1st.hatenablog.jp/entry/2016/12/16/183718
https://duckduckgo.com/?q=スーパー牛さんパワー
https://qiita.com/kon_yu/items/b8864ff566b8b67a9810
https://qiita.com/mkamotsu/items/0135d2e756db4a18d965
https://qiita.com/g_maeda/items/3602dc2f7af3f711f9c4
https://qiita.com/takaki@github/items/5d96d80dc78b09ea600b

</div></details>

# デスクトップ用途での LVM の利点として思いつくこと
色々インストールしたりダウンロードしたりコンパイルしたり好き放題していたらいつの間にか容量が足りない！という場合に少ない手間で容量を[継ぎ足せます](https://duckduckgo.com/?q=何にでも牛乳を注ぐ女)。

# 基本工程
・[OS インストール環境の立ち上げ](https://qiita.com/EarthSimilarityIndex/items/89a482dd9c50401f5cf4#os-%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB%E7%92%B0%E5%A2%83%E3%81%AE%E7%AB%8B%E3%81%A1%E4%B8%8A%E3%81%92)
・[GPT でフォーマット](https://qiita.com/EarthSimilarityIndex/items/89a482dd9c50401f5cf4#gpt-%E3%81%A7%E3%83%95%E3%82%A9%E3%83%BC%E3%83%9E%E3%83%83%E3%83%88)
・[LVM パーティションの作成](https://qiita.com/EarthSimilarityIndex/items/89a482dd9c50401f5cf4#lvm-%E3%83%91%E3%83%BC%E3%83%86%E3%82%A3%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%AE%E4%BD%9C%E6%88%90)
[・LVM 論理ボリュームの作成](https://qiita.com/EarthSimilarityIndex/items/89a482dd9c50401f5cf4#lvm-%E8%AB%96%E7%90%86%E3%83%9C%E3%83%AA%E3%83%A5%E3%83%BC%E3%83%A0%E3%81%AE%E4%BD%9C%E6%88%90)
・[通常の OS インストール手順](https://qiita.com/EarthSimilarityIndex/items/89a482dd9c50401f5cf4#%E9%80%9A%E5%B8%B8%E3%81%AE-os-%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB%E6%89%8B%E9%A0%86)
・[ブートローダーの LVM 対応](https://qiita.com/EarthSimilarityIndex/items/89a482dd9c50401f5cf4#os-%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB%E7%92%B0%E5%A2%83%E3%81%AB%E3%81%8A%E3%81%91%E3%82%8B%E3%83%96%E3%83%BC%E3%83%88%E3%83%AD%E3%83%BC%E3%83%80%E3%83%BC%E3%81%AE-lvm-%E5%AF%BE%E5%BF%9C)
・[再起動](https://qiita.com/EarthSimilarityIndex/items/89a482dd9c50401f5cf4#%E5%86%8D%E8%B5%B7%E5%8B%95)

# インストール時に HDD/SSD/swap を暗号化したい場合
LVM/Luks Encryption - SlackWiki
https://www.slackwiki.com/LVM/Luks_Encryption
↑こちらをご覧下さい。

※ EFI システムパーティションをこの方法で暗号化するのは無理っぽいです。改竄防止などは BIOS/UEFI 側の機能で行えるのかな？
※ Fat32 の EFI システムパーティションってセキュリティ的にはどうなんだろ？[意外と強い](https://ja.wikipedia.org/wiki/%E3%82%B7%E3%83%A3%E3%82%B3)のか？

# 各工程の詳細
## OS インストール環境の立ち上げ
### インストールメディアの用意
**！！！CAUTION！！！イメージを書き込むドライブを間違えないこと！**
dd で空の USB メモリに インストール DVD の iso を流し込むのが楽です。

インストールイメージ入手先
http://www.slackware.com/getslack/
### インストールメディアからブート
UEFI ブート。その際、インストール対象 HDD/SSD は IDE 互換モードではなく AHCI モードにしておく[^reason]。

[^reason]: なんとなくドライブの潜在能力がもったいない気がするので。
## GPT でフォーマット
**！！！CAUTION！！！フォーマットするドライブを間違えないこと！**

```
# fdisk -l
# sgdisk -o /dev/moo
```

※ /dev/moo は環境に応じて適宜読み替えて下さい。

<details><summary>参考文献：</summary><div>

Man page of FDISK
https://linuxjm.osdn.jp/html/util-linux/man8/fdisk.8.html

Man page of SGDISK
http://www.rodsbooks.com/gdisk/sgdisk.html

Linux におけるデバイス名
https://www.debian.org/releases/stable/i386/apcs04.ja.html

</div></details>

## LVM パーティションの作成

```
# sgdisk -p /dev/moo
# sgdisk -n 1:2048:1050623 -c 1:"ESP" -t 1:ef00 /dev/moo
# sgdisk -n 2:1050624:3147775 -c 2:"swap" -t 2:8200 /dev/moo
# sgdisk -n 3:3147776:$(sgdisk -E /dev/sda) -c 3:"LVM00" -t 3:8e00 /dev/moo
# sgdisk -p /dev/moo
```

※ /dev/moo は環境に応じて適宜読み替えて下さい。
※ EFIシステムパーティション（ESP）は LVM の外に。
※ Swap パーティションは LVM の中でも外でも（ ~~中に置くと swsusp させる場合 mkinitrd で一工夫必要~~ [swsusp させる場合は mkinitrd で -h オプションを使うのが無難](https://docs.slackware.com/howtos:slackware_admin:hibernation)）。
※ 各パーティションに割り当てる容量はシステム環境や管理方針によります。
※ 2048 セクタ:1 MiB（1 セクタ 512 バイトの場合）
※ 1024 MiB = 1 GiB
※ 1024 GiB = 1 TiB
※ メモ書きからのコピペやシェルスクリプトに記述する用途では sgdisk 。通常使うなら対話的な gdisk や cgdisk が使いやすいかと。

<details><summary>参考文献：</summary><div>

Man page of SGDISK
http://www.rodsbooks.com/gdisk/sgdisk.html

An sgdisk Walkthrough
http://www.rodsbooks.com/gdisk/sgdisk-walkthrough.html

sgdisk(gdisk コマンドライン版) でまとめて変更する使い方 - それマグで！
https://takuya-1st.hatenablog.jp/entry/2016/12/16/183718

bash Tips - コマンド置換と算術式展開、パラメータ展開 - Qiita
https://qiita.com/mashumashu/items/bbc3a79bc779fe8c4f99

Man page of BASH
https://linuxjm.osdn.jp/html/GNU_bash/man1/bash.1.html

[メビバイト - Wikipedia](https://ja.wikipedia.org/wiki/%E3%83%A1%E3%83%93%E3%83%90%E3%82%A4%E3%83%88)

</div></details>

## LVM 論理ボリュームの作成

```
# pvcreate /dev/moo3
# vgcreate SlackVG /dev/moo3
# lvcreate -l 100%FREE SlackVG -n root
```

※ /dev/moo3 SlackVG root は環境や用途、sgdisk の実行状況等に応じて適宜読み替えて下さい。
※ pv=PhysicalVolume vg=VolumeGroup lv=LogicalVolume 略語のもとになった言葉。
<details><summary>参考文献：</summary><div>

pvcreate(8) - Linux man page
https://linux.die.net/man/8/pvcreate

vgcreate(8) - Linux man page
https://linux.die.net/man/8/vgcreate

lvcreate(8) - Linux man page
https://linux.die.net/man/8/lvcreate

LVM/Luks Encryption - SlackWiki
https://www.slackwiki.com/LVM/Luks_Encryption

LVM - ArchWiki
https://wiki.archlinux.jp/index.php/LVM

</div></details>

## 通常の OS インストール手順

```
# setup
```

※ 完了時にうっかり再起動をかけないこと。

![VirtualBox_Slackware_14.2_113.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/410379/aea9549c-365d-6ae2-26d8-cbd4deadee22.png)

参考文献：
http://www.slackware.com/install/setup.php

## OS インストール環境におけるブートローダーの LVM 対応
![VirtualBox_Slackware64_10_07_2019_16_42_24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/410379/5d66152f-45de-e23a-a6b7-79ed15a1e538.png)

```
# chroot /mnt
# $(/usr/share/mkinitrd/mkinitrd_command_generator.sh -r)
# exit
# eliloconfig /mnt /dev/SlackVG/root
```

※ mkinitrd_command_generator.sh は mkinitrd に対する適切な引数を自動生成するコマンドです。詳細は ```/usr/share/mkinitrd/mkinitrd_command_generator.sh --longhelp``` で確認。
※ eliloconfig は引数1で OS ルートを、引数2でルートに対応するデバイスを指定出来るようです。
※ /dev/SlackVG/root は作成した論理ボリュームに応じて適宜読み替えて下さい。
※ 再起動後、インストール済みの Slackware からは eliloconfig に引数を与えなくとも正しく /boot/efi/EFI/Slackware/elilo.conf を生成出来ました。
※ カーネルのアップグレードを行った場合は忘れずに ```$(/usr/share/mkinitrd/mkinitrd_command_generator.sh -r)``` 及び ```eliloconfig``` を実行すること。インストール済みの Slackware で行う場合 chroot の必要はありません。

<details><summary>参考文献：</summary><div>

bash Tips - コマンド置換と算術式展開、パラメータ展開 - Qiita
https://qiita.com/mashumashu/items/bbc3a79bc779fe8c4f99

Man page of BASH
https://linuxjm.osdn.jp/html/GNU_bash/man1/bash.1.html

GNU Coreutils: 23.1 chroot: ルートディレクトリを変更して、コマンドを実行する
https://linuxjm.osdn.jp/info/GNU_coreutils/coreutils-ja_169.html#chroot-invocation

Chroot - Gentoo Wiki
https://wiki.gentoo.org/wiki/Chroot/ja

chroot - ArchWiki
https://wiki.archlinux.jp/index.php/Chroot

LVM/Luks Encryption - SlackWiki
https://www.slackwiki.com/LVM/Luks_Encryption

[SOLVED] Slackware-current elilo lvm
https://www.linuxquestions.org/questions/slackware-14/slackware-current-elilo-lvm-4175534140/

<details><summary>eliloconfig 本体</summary><div>

```sh:/usr/sbin/eliloconfig
#!/bin/sh
# Copyright 2013, 2016, 2017  Patrick Volkerding, Sebeka, Minnesota, USA
# All rights reserved.
#
# Redistribution and use of this script, with or without modification, is
# permitted provided that the following conditions are met:
#
# 1. Redistributions of this script must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
#
#  THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
#  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
#  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO
#  EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
#  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
#  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
#  OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
#  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
#  OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
#  ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#

# This script installs the elilo boot loader, the kernel, and optionally
# an initrd into the EFI System Partition.  A boot menu entry can also be
# installed using efibootmgr.  The kernel used will be whatever the symlink
# /boot/vmlinuz points to, and if /boot/initrd.gz exists, that will be 
# installed as the initrd.
#
# The EFI System Partition is expected to be mounted on /boot/efi before
# running this script.

TMP=/var/log/setup/tmp

# Set the OS root directory (called T_PX for some unknown reason).
# If an argument is given to this script and it is a directory, it
# is taken to be the root directory.  First though, we check for a
# directory named $T_PX, and that gets the first priority.
if [ ! -d "$T_PX" ]; then
  if [ ! "$1" = "" ]; then
    if [ -d "$1" ]; then
      T_PX="$1"
    fi
  else
    # Are we on the installer image?
    if [ -r /usr/lib/setup/SeTpartitions ]; then
      T_PX=/mnt
    # Or, are we on a running system?
    elif [ -r /etc/slackware-version ]; then
      T_PX=/
    # One more installer-likely thing:
    elif [ -r /usr/lib/setup/setup ]; then
      T_PX=/mnt
    else
      # We will have to assume we're on an installed and running system.
      T_PX=/
    fi
  fi
fi

# Determine the root partition (such as /dev/sda1)
ROOT_DEVICE=$2
if [ "$ROOT_DEVICE" = "" ]; then
 if [ -r $TMP/SeTrootdev ]; then
  ROOT_DEVICE="$(cat $TMP/SeTrootdev)"
 else
  ROOT_DEVICE="$(mount | cut -f 1 -d ' ' | head -n 1)"
 fi
fi

# If the system is not running under EFI, skip running this script:
if [ ! -d /sys/firmware/efi ]; then
  if [ "$T_PX" = "/" ]; then
    echo "ERROR:  System is not running under UEFI."
  fi
  exit
fi

# If there's no vfat mounted on /boot/efi or $T_PX/boot/efi, forget it:
if ! mount | grep vfat | grep -wq /boot/efi ; then
  if ! mount | grep vfat | grep -wq $T_PX/boot/efi ; then
    if [ "$T_PX" = "/" ]; then
      echo "ERROR:  No EFI System Partition mounted on /boot/efi."
    fi
    exit
  fi
fi

# Figure out the device and partition number of the ESP:
DEVLEN=8
PARTLEN=9
if mount | grep vfat | grep -wq /boot/efi ; then
  mount | grep vfat | grep -w /boot/efi | grep -q -e nvme -e mmcblk && DEVLEN=12 && PARTLEN=14
  EFI_DEVICE=$(mount | grep vfat | grep -w /boot/efi | cut -b 1-${DEVLEN})
  EFI_PARTITION=$(mount | grep vfat | grep -w /boot/efi | cut -f 1 -d ' ' | cut -b ${PARTLEN}- | tr -d [a-z])
else
  mount | grep vfat | grep -w $T_PX/boot/efi | grep -q -e nvme -e mmcblk && DEVLEN=12 && PARTLEN=14
  EFI_DEVICE=$(mount | grep vfat | grep -w $T_PX/boot/efi | cut -b 1-${DEVLEN})
  EFI_PARTITION=$(mount | grep vfat | grep -w $T_PX/boot/efi | cut -f 1 -d ' ' | cut -b ${PARTLEN}- | tr -d [a-z])
fi

# There better be a kernel:
if [ ! -r $T_PX/boot/vmlinuz ]; then
  if [ "$T_PX" = "/" ]; then
    echo "ERROR:  No kernel found at /boot/vmlinuz."
  fi
  exit
fi

dialog --title "INSTALL ELILO" \
--backtitle "ELILO (EFI Linux Loader) installation" \
--menu "ELILO is a Linux boot loader for \
EFI based systems.  Installing ELILO will allow you to boot your Linux \
system from the hard drive after selecting it from the UEFI boot menu.  \
Since an EFI System Partition was detected on this machine, it is \
recommended that you install ELILO now.  Please select an option:" \
13 70 2 \
"install" "Install ELILO on the EFI System Partition" \
"skip" "Do not install ELILO" 2> $TMP/reply
if [ $? = 1 -o $? = 255 ]; then
  exit
fi
REPLY="`cat $TMP/reply`"
rm -f $TMP/reply
if [ "$REPLY" = "skip" ]; then
  exit
fi

mkdir -p $T_PX/boot/efi/EFI/Slackware 1> /dev/null 2> /dev/null
cat << EOF > $T_PX/boot/efi/EFI/Slackware/elilo.conf
chooser=simple
delay=1
timeout=1
#
EOF
cp -a $T_PX/boot/elilo-x86_64.efi $T_PX/boot/efi/EFI/Slackware/elilo.efi 1> /dev/null 2> /dev/null
cp $T_PX/boot/vmlinuz $T_PX/boot/efi/EFI/Slackware 1> /dev/null 2> /dev/null
cat << EOF >> $T_PX/boot/efi/EFI/Slackware/elilo.conf
image=vmlinuz
        label=vmlinuz
EOF
touch -r $T_PX/boot/vmlinuz $T_PX/boot/efi/EFI/Slackware/vmlinuz
if [ -r $T_PX/boot/initrd.gz ]; then
  cp $T_PX/boot/initrd.gz $T_PX/boot/efi/EFI/Slackware 1> /dev/null 2> /dev/null
  touch -r $T_PX/boot/initrd.gz $T_PX/boot/efi/EFI/Slackware/initrd.gz
  cat << EOF >> $T_PX/boot/efi/EFI/Slackware/elilo.conf
        initrd=initrd.gz
EOF
fi
cat << EOF >> $T_PX/boot/efi/EFI/Slackware/elilo.conf
        read-only
        append="root=$ROOT_DEVICE vga=normal ro"
EOF

dialog --title "INSTALL BOOT MENU ENTRY?" \
--backtitle "ELILO (EFI Linux Loader) installation" \
--menu "Would you like to install \
a boot menu entry so that you can easily select Slackware when you boot your \
machine?  WARNING:  DO NOT install a boot menu entry on Apple hardware.  \
Apple computers use a different type of EFI firmware, and efibootmgr has \
been known to damage them.  On all other computers, it is recommended to add \
a boot menu entry.  Please select an option:" \
14 70 2 \
"install" "Install a boot menu entry" \
"skip" "Do not install a boot menu entry" 2> $TMP/reply
if [ $? = 1 -o $? = 255 ]; then
  exit
fi
REPLY="`cat $TMP/reply`"
rm -f $TMP/reply
if [ "$REPLY" = "skip" ]; then
  exit
fi

if [ ! -d /sys/firmware/efi/vars ]; then
  modprobe efivars 1> /dev/null 2> /dev/null
  sleep 1 # Probably not needed, but playing it safe
fi
# If it's still gone, we fail silently:
if [ ! -d /sys/firmware/efi/vars ]; then
  exit
fi

efibootmgr -v | rev | cut -f 2- | rev | grep Boot0 | grep Slackware | while read line ; do
  # Reject entries that don't exactly match as not our business
  if [ ! "$(echo $line | cut -f 2- -d ' ')" = "Slackware" ]; then
    continue
  fi
  ENTRY="$(efibootmgr -v | grep "^$(echo $line | cut -b1-8)")"
  dialog --title "REMOVE OLD \"Slackware\" EFI BOOT ENTRY?" \
  --backtitle "ELILO (EFI Linux Loader) installation" \
  --yesno "An old Slackware boot entry has been found in your EFI boot menu. \
Would you like to remove this before installing the new Slackware boot \
entry?  This is recommended.\n\nOld EFI boot menu entry:\n$ENTRY" 13 70
  if [ ! $? = 0 ]; then
    continue
  fi
  # Remove the boot entry:
  efibootmgr -q -B -b $(echo $line | cut -b5-8)
done

efibootmgr -q -c -d $EFI_DEVICE -p $EFI_PARTITION -l "\\EFI\\Slackware\\elilo.efi" -L "Slackware"

efibootmgr -v | rev | cut -f 2- | rev | grep Boot0 | grep Slackware | while read line ; do
  # Reject entries that don't exactly match as not our business
  if [ ! "$(echo $line | cut -f 2- -d ' ')" = "Slackware" ]; then
    continue
  fi
  ENTRY="$(efibootmgr -v | grep "^$(echo $line | cut -b1-8)")"
  if echo $ENTRY | grep -q -F "\EFI\Slackware\elilo.efi" ; then
  dialog --title "EFI BOOT ENTRY INSTALLED" \
  --backtitle "ELILO (EFI Linux Loader) installation" \
  --msgbox "A Slackware boot entry has been installed to your EFI boot
menu.\n\nEFI boot menu entry:\n$ENTRY" 11 70
  fi
done

# Done.
```
</div></details>

<details><summary>mkinitrd_command_generator.sh 本体</summary><div>

```sh:/usr/share/mkinitrd/mkinitrd_command_generator.sh
#!/bin/sh
# $Id: mkinitrd_command_generator.sh,v 1.45 2011/02/17 09:27:05 eha Exp eha $
# Copyright 2013  Patrick J. Volkerding, Sebeka, Minnesota, USA
# Copyright 2008, 2009, 2010, 2011  Eric Hameleers, Eindhoven, Netherlands
#
# Copyright 2008, 2009  PiterPUNK, Sao Paulo, SP, Brazil
#
# All rights reserved.
#
#   Permission to use, copy, modify, and distribute this software for
#   any purpose with or without fee is hereby granted, provided that
#   the above copyright notice and this permission notice appear in all
#   copies.
#
#   THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
#   WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
#   MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
#   IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR
#   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
#   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
#   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
#   USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
#   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
#   OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
#   OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
#   SUCH DAMAGE.
# -----------------------------------------------------------------------------
#
# Create an initrd which fits the system.
# Take into account the use of LVM/LUKS/RAID.
# Find out about any hardware drivers the system may need in an initrd when
# booting from a generic lightweight kernel.
#
# -----------------------------------------------------------------------------

# The script's revision number will be displayed in the help text:
REV=$( echo "$Revision: 1.45 $" | cut -d' '  -f2 )

# Define some essential parameter values:
USING_LVM=""
USING_LUKS=""
USING_RAID=""
MLIST=""
REALDEV=""   # The device that contains the LUKS volume
BASEDEV=""   # Lowest level device (raw block device or RAID volume)

FSTAB=${FSTAB:-"/etc/fstab"} # so we can test with alternate fstab files

# These are needed by -c and -i options:
SOURCE_TREE=${SOURCE_TREE:-"/boot/initrd-tree"}
CLEAR_TREE=${CLEAR_TREE:-1}
KEYMAP=${KEYMAP:-"us"}
UDEV=${UDEV:-1}
# ARM devices need more time:
case "$( uname -m )" in
  arm*) WAIT_DEFAULT=4;;
  *) WAIT_DEFAULT=1;;
esac
WAIT=${WAIT:-$WAIT_DEFAULT}

# A basic explanation of the commandline parameters:
basic_usage() {
  cat <<-EOT
	
	*** $(basename $0) revision $REV ***
	Usage:
	  $(basename $0) [ options ] [ kernel_filename ]
	Options:
	  -a <"additional params">  Additional parameters to pass to mkinitrd.
	  -c | --conf               Show a suitable mkinitrd configuration file.
	  -h | --help               Show this help.
	  -i | --interactive        Navigate through menus instead of using
	                            commandline arguments.  
	  --longhelp                Show more detailed information/examples.
	  -k <kernelversion>        Use specific kernel version.
	  -m <"additional mods">    Additional modules to pass to mkinitrd,
	                            separated by colons (:).
	  -l | --lilo               Only show lilo.conf section
	                            (requires a kernel_filename).
	  -r | --run                Only show 'mkinitrd' command.
	EOT
}

# More of a tutorial here:
extended_usage() {
  cat <<-EOT
	
	This script is useful in situations where you require an initrd image
	to boot your computer.
	For instance, when booting a kernel that does not have support for your
	storage or root filesystem built in (such as the Slackware 'generic'
	kernels').
	
	* When you run the script without parameters, it will examine your
	running system, your current kernel version and will output an example
	of a 'mkinitrd' commandline that you can use to generate an initrd
	image containing enough driver support to boot the computer.
	
	* You can make it more specific: when you add the filename of a kernel
	as parameter to the script, it will determine the kernel version from
	that kernel, and also give an example of the lines that you should add
	to your '/etc/lilo.conf' file.
	
	* If you want your initrd image to have a custom name instead of the
	default '/boot/initrd.gz' you can add it as another parameter to the
	script, as follows:
	
	  $(basename $0) -a "-o /boot/custom.gz"
	
	The arguments to the '-a' parameter will be used as additional arguments
	to the 'mkinitrd' command.
	
	* If you need additional modules in the initrd image, apart from what
	the script determines, you can pass then to the script using the '-m'
	parameter as follows:
	
	  $(basename $0) -m "uhci-hcd:usbhid:hid_generic"
	
	The above example adds support for USB keyboards to the initrd - you
	may need that if you have encrypted your root partition and need to
	enter a passphrase using a USB keyboard.
	
	* Instead of copying and pasting the script's output, you can create
	an initrd by directly executing the output, like in this example:
	
	EOT

  echo "  \$($(basename $0) --run /boot/vmlinuz-generic-smp-2.6.35.11-smp)"

  cat <<-EOT
	
	That used the '-r' or '--run' switch to make the script only write
	the 'mkinitrd' commandline to the terminal.
	
	* When you want to add a section for a specific kernel to
	'/etc/lilo.conf' directly, use the '-l' or '--lilo' switch and use a
	command like in this example:
	
	EOT

  echo "  $(basename $0) --lilo /boot/vmlinuz-generic-smp-2.6.35.11-smp >>/etc/lilo.conf"

  cat <<-EOT
	
	That command will result in the following lines being added to your
	'/etc/lilo.conf' file (example for my hardware):
	
	  # Linux bootable partition config begins
	  # initrd created with 'mkinitrd -c -k 2.6.35.11-smp -m ata_generic:pata_amd:mbcache:jbd:ext3 -f ext3 -r /dev/hda7'
	  image = /boot/vmlinuz-generic-smp-2.6.35.11-smp
	    initrd = /boot/initrd.gz
	    root = /dev/hda7
	    label = 2.6.35.11-smp
	    read-only
	  # Linux bootable partition config ends
	
	The last two examples show how easy it is to configure your computer
	for the use of an initrd if you need one. The only thing left to do
	afterwards is running 'lilo'.
	
	EOT
}

# Find the device that holds the root partition:
get_root_device() {
  if [ -e $FSTAB ]; then
    RD=$(cat $FSTAB |tr '\t' ' ' |grep -v '^ *#' |tr -s ' ' |grep ' / ' |cut -f1 -d' ')
    if [ "$(echo $RD | cut -f1 -d=)" = "LABEL" -o "$(echo $RD | cut -f1 -d=)" = "UUID" ]; then
      DKEY=$(echo $RD | cut -f1 -d=)
      # The value can be LABEL=foo or LABEL='foo' or LABEL="foo"
      DVAL=$(echo $RD | cut -f2 -d= | tr -d "'\042")
      RD=$(/sbin/blkid | grep -w $DKEY | grep -w $DVAL | cut -f1 -d:)
    fi
  else
    RD=$(grep -m1 "^/dev/.*[[:blank:]]/[[:blank:]]" /proc/mounts | cut -f1 -d' ')
  fi
  echo $RD
}

# Get the root fs information:
get_rootfs_type() {
  if $(type blkid 1>/dev/null 2>&1) ; then
   blkid -s TYPE -o value $ROOTDEV
  elif $(type vol_id 1>/dev/null 2>&1) ; then
   vol_id $ROOTDEV | grep ID_FS_TYPE | cut -f2 -d=
  else
   # As a fallback, use:
   cat $FSTAB |tr '\t' ' ' |grep -v '^ *#' |tr -s ' ' |grep ' / ' |cut -f3 -d' '
  fi
}

# Add the module(s) needed for the root filesystem:
add_rootfs_module() {
  local FSMOD
  FSMOD=$(/sbin/modprobe --set-version $KVER --show-depends ${ROOTFS} 2>/dev/null | while read LINE; do
    echo $(basename $(echo $LINE | cut -d' ' -f2) .ko )
  done)
  if [ -n "$FSMOD" ]; then
     [ -n "$MLIST" ] && echo "$MLIST:$(echo $FSMOD | tr ' ' ':')" \
                    || echo $FSMOD | tr ' ' ':'
  fi
}

# Determine the list of kernel modules needed to support the root device:
determine_blockdev_drivers() {
  # Walk the /sys tree to find kernel modules that are
  # required for our storage devices.
  # Thanks to PiterPUNK for help with this code.
  local MLIST
  MLIST=$(for i in $(find /sys/block/*/ -name "device" -print0 | xargs -0 -i'{}' readlink -f '{}' | sort -u); do
    /sbin/udevadm info --query=all --path=$i --attribute-walk | \
      sed -ne 's/^[[:blank:]]\+DRIVER[S]*=="\([^"]\+\)"$/\1/p' | \
      xargs -I@ /sbin/modprobe --set-version $KVER --show-depends @ \
      2>/dev/null | grep -v "builtin " | \
      while read LINE ; do 
        echo $(basename $(echo $LINE | cut -d' ' -f2) .ko )
      done
  done)
  MLIST=$( echo $MLIST | tr ' ' ':' )
  echo $MLIST
}

# Search for USB keyboards:
function add_usb_keyboard() {
  local USBMOD
  if cat /proc/bus/input/devices | sed -e 's/^$/\$/g' | \
     tr "\n$" " \n" | grep -q " Phys=.*usb.* .*Handlers=.*kbd.*B:"; then
     USBMOD="xhci-pci:ohci-pci:ehci-pci:xhci-hcd:uhci-hcd:ehci-hcd:hid:usbhid:i2c-hid:hid_generic:hid-cherry:hid-logitech:hid-logitech-dj:hid-logitech-hidpp:hid-lenovo:hid-microsoft:hid_multitouch"
     [ -n "$MLIST" ] && MLIST="$MLIST:$USBMOD" \
                     || MLIST="$USBMOD"
  fi
  echo $MLIST
}

# Determine what USB Host Controller is in use
function add_usb_hcd() {
  local USBMOD
  for i in $(ls -Ld /sys/module/*_hcd/drivers/* 2> /dev/null); do 
    if ls -L $i | grep -q "[0-9a-f]*:" ; then
      USBMOD=$( echo $i | cut -f4 -d/ | tr "_" "-")
      [ -n "$MLIST" ] && MLIST="$MLIST:$USBMOD" \
                      || MLIST="$USBMOD"
    fi
  done
  echo $MLIST
}

# Is the root partition on a (combination of) LVM/LUKS volume?
check_luks_lvm_raid() {
  if $( lvdisplay -c $ROOTDEV 1>/dev/null 2>/dev/null ); then
    # Our root partition is on a LV:
    USING_LVM=1
    # Search the Physical Volume of our Logical Volume:
    MYVG=$( echo $(lvdisplay -c $ROOTDEV 2>/dev/null) | cut -d: -f2 )
    for LINE in $(pvdisplay -c) ; do
      VG=$(echo $LINE | cut -d: -f2)
      [ "$VG" = "$MYVG" ] && break
    done 
    PV=$(echo $LINE | cut -d: -f1)
    # Check if there is a LUKS device underneath:
    if $( cryptsetup status $PV 1>/dev/null 2>/dev/null ) ; then
      # Our root partition's LV  is on a LUKS volume:
      USING_LUKS=1
      REALDEV=$( cryptsetup status $PV | grep 'device: ' | tr -d ' ' | cut -d: -f2 )
      BASEDEV=$REALDEV
    else
      BASEDEV=$PV
    fi
  elif $( cryptsetup status $ROOTDEV 1>/dev/null 2>/dev/null ) ; then
    # Our root device is on a LUKS volume:
    USING_LUKS=1
    REALDEV=$( cryptsetup status $ROOTDEV | grep 'device: ' | tr -d ' ' | cut -d: -f2 )
    ROOTDEV=$(basename $ROOTDEV)
    # Check for LVM:
    for LV in $(lvdisplay -c 2>/dev/null | tr -d ' ' | cut -f1 -d:) ; do
      # Note: cryptsetup shows the real device, whereas 
      # lvdisplay requires the /dev/<myvg>/... symlink to the real device.
      if [ "$(readlink $LV)" = "$REALDEV" ]; then
        REALDEV=$LV
        break
      fi
    done
    if $( lvdisplay -c $REALDEV 1>/dev/null 2>/dev/null ); then
      # Our root partition's LUKS device is on a LV:
      USING_LVM=1
      # Search the Physical Volume of our Logical Volume:
      MYVG=$( echo $(lvdisplay -c $REALDEV 2>/dev/null) | cut -d: -f2 )
      for LINE in $(pvdisplay -c) ; do
        VG=$(echo $LINE | cut -d: -f2)
        [ "$VG" = "$MYVG" ] && break
      done 
      PV=$(echo $LINE | cut -d: -f1)
      BASEDEV=$PV
    else
      BASEDEV=$REALDEV
    fi
  else
    BASEDEV=$ROOTDEV
  fi

  # Finally, we should check if base device is
  #   a real block device or a RAID volume:
  for MD in  $(cat /proc/mdstat | grep -w active | cut -d' ' -f1) ; do
    if [ "$BASEDEV" = "/dev/$MD" ]; then
      USING_RAID=1
      break
    fi
  done
}

# Before we start
[ -x /bin/id ] && CMD_ID="/bin/id" || CMD_ID="/usr/bin/id"
if [ "$($CMD_ID -u)" != "0" ]; then
  echo "You need to be root to run $(basename $0)."
  exit 1
fi

# Parse the commandline parameters:
while [ ! -z "$1" ]; do
  case $1 in
    --longhelp)
      basic_usage
      extended_usage
      exit 0
      ;;
    -a)
      MKINIT_PARAMS="$2"
      shift 2
      ;;
    -c|--conf)
      [ -n "$EMIT" ] && { echo "Do not mix incompatible parameters!"; exit 1; }
      EMIT="conf"
      shift
      ;;
    -h|--help)
      basic_usage
      exit 0
      ;;
    -i|--interactive)
      INTERACTIVE=1
      shift
      ;;
    -k)
      KVER=$2
      shift 2
      ;;
    -m)
      MKINIT_MODS=$2
      shift 2
      ;;
    -l|--lilo)
      [ -n "$EMIT" ] && { echo "Do not mix incompatible parameters!"; exit 1; }
      EMIT="lilo"
      shift
      ;;
    -L|--fromlilo)
      FROMLILO=1
      shift
      ;;
    -r|--run)
      [ -n "$EMIT" ] && { echo "Do not mix incompatible parameters!"; exit 1; }
      EMIT="run"
      shift
      ;;
    -R|--rootdev)
      ROOTDEV=$2
      shift 2
      ;;
    -*)
      echo "Unsupported parameter '$1'!"
      exit 1
      ;;
    *) # Everything else but switches (which start with '-') follows:
      if [ -f $1 ]; then
        KFILE=$1
        # Construction of KFILE's full filename:
        KFILEPATH=$(cd $(dirname $KFILE) && pwd)
        if [ -L $KFILE ]; then
          KFILE=$(readlink $KFILE)
        else
          KFILE=$(basename $KFILE)
        fi
        KFILE=${KFILEPATH}/$KFILE
        if [ -z "$(file $KFILE | grep -E 'Linux kernel x86 boot|x86 boot sector')" ]; then
          echo "File '$KFILE' does not look like it is a kernel file!"
          exit 1
        fi
      else
        echo "File $1 not found!"
        exit 1
      fi   
      shift
      ;;
  esac
done

# Determine what to show as output (other options may have set EMIT already)
EMIT=${EMIT:-"all"}

# An EMIT value of 'lilo' requires a kernel filename as script parameter:
if [ "$EMIT" = "lilo" ]; then
  if [ -z "$KFILE" ]; then
    echo "A kernel_filename is required with the '-l|--lilo' option!"
    exit 1
  fi
fi

# Determine kernel version to use,
# and check if modules for this kernel are actually present:
if [ -z "$KVER" ]; then
  if [ -n "$KFILE" ]; then
    KVER="$(strings $KFILE | grep '([^ ]*@[^ ]*) #' | cut -f1 -d' ')"
  else
    KVER="$(uname -r)"
  fi
fi
if [ ! -d /lib/modules/$KVER ]; then
  echo "Modules for kernel $KVER aren't installed."
  exit 1
fi  

# Determine whether the user passed an alternate filename for the initrd:
if [ -n "$MKINIT_PARAMS" ]; then
  SRCHLIST="$MKINIT_PARAMS"
  for ELEM in $MKINIT_PARAMS ; do
    SRCHLIST=$(echo $SRCHLIST | cut -d' ' -f2-) # cut ELEM from the list
    if [ "$ELEM" = "-o" ]; then
      IMGFILE=$(echo $SRCHLIST | cut -d' ' -f1)
      break
    fi
  done
fi
IMGFILE=${IMGFILE:-"/boot/initrd.gz"}

# Get information about the root device / root filesystem:
ROOTDEV=${ROOTDEV:-$(get_root_device)}
ROOTFS=$(get_rootfs_type)

# Determine the list of kernel modules needed to support the root device:
MLIST=$(determine_blockdev_drivers)

# Check if we are running in a kvm guest with virtio block device driver
# (add all virtio modules, we sort out the doubles later):
if echo $MLIST | grep -q "virtio"; then
  MLIST="$MLIST:virtio:virtio_balloon:virtio_blk:virtio_ring:virtio_pci:virtio_net"
fi

# Determine if a USB keyboard is in use and include usbhid and hid_generic
# to module list
MLIST=$(add_usb_keyboard)

# If we use any USB module, try to determine the Host Controller
if echo $MLIST | grep -q "usb"; then
  MLIST=$(add_usb_hcd)
fi

# Check what combination of LUKS/LVM/RAID we have to support:
# This sets values for USING_LUKS, USING_LVM, USING_RAID, REALDEV and BASEDEV.
check_luks_lvm_raid

# This is the interactive part:
if [ "$INTERACTIVE" = "1" ];  then
  if [ "$FROMLILO" != "1" ]; then
    dialog --stdout --title "WELCOME TO MKINITRD COMMAND GENERATOR" --msgbox "\
The main goal of this utility is to create a good initrd to \
fit your needs.  It can detect what kernel you are running, \
what is your root device, root filesystem, if you use encryption, \
LVM, RAID, etc. \
\n\n\
Usually the probed values are OK and they will be the \
defaults in all subsequent dialogs, but maybe you want \
to change something. \n\
If in doubt, leave the defaults." 0 0 

    KVER=$( ls -d1 --indicator-style=none /lib/modules/* | \
      awk -F/ -vVER=$KVER '{ 
                            if ( VER == $NF ) { 
                              ONOFF="on" 
                            } else { 
                              ONOFF="off" 
                            } ; printf("%s \"\" %s\n",$NF,ONOFF) }' | \
      xargs dialog --stdout --title "CHOOSE KERNEL VERSION" \
                   --default-item $KVER --radiolist "\
Please, select the kernel version you want to create this initrd for." 0 0 4 )
    [ -z "$KVER" ] && exit 1

    OLDROOTDEV=$ROOTDEV
    ROOTDEV=$( dialog --stdout --title "SELECT ROOT DEVICE" --inputbox "\
Enter your root device. Root device is the one where your '/' filesystem \
is mounted." 0 0 "$ROOTDEV" )
    [ -z "$ROOTDEV" ] && exit 1

    # We need to re-check our defaults in case the user changed the default
    # value for ROOTDEV:
    [ "$OLDROOTDEV" != "$ROOTDEV" ] && check_luks_lvm_raid
    ROOTFS=$(get_rootfs_type)

    ROOTFS=$( dialog --stdout --title "SELECT ROOT FILESYSTEM" --inputbox "\
Enter the type of your root filesystem." 0 0 "$ROOTFS" )
    [ -z "$ROOTFS" ] && exit 1
  fi

  MLIST=$(add_rootfs_module)

  LLR=$( dialog --stdout --title "LVM/LUKS/RAID" --checklist "\
Do you use some of those in your root filesystem?  \
If this is the case, please select one or more options." 12 45 3 \
"LVM" "Logical Volume Manager" $([ "$USING_LVM" = "1" ] && echo on || echo off) \
"LUKS" "Linux Unified Key Setup" $([ "$USING_LUKS" = "1" ] && echo on || echo off) \
"RAID" "Linux Software RAID" $([ "$USING_RAID" = "1" ] && echo on || echo off)) 

  if [ "$?" != "0" ]; then
    exit 1
  fi

  echo $LLR | grep -q LUKS && USING_LUKS="1"
  echo $LLR | grep -q LVM && USING_LVM="1"
  echo $LLR | grep -q RAID && USING_RAID="1"

  if [ "$USING_LUKS" = "1" ]; then
    REALDEV=$( dialog --stdout --title "LUKS ROOT DEVICE" --inputbox "\
Please, enter your LUKS root device:" 0 0 "$REALDEV" )
    [ -z "$REALDEV" ] && exit 1
  fi
fi

# Step out of the interactive loop for a moment. The next block needs to be
# executed in all cases.

# We need to 'undouble' the MLIST array. Some people report that walking the
# /sys tree produces duplicate modules in the list.
# The awk command elimitates doubles without changing the order:
MLIST=$( echo $MLIST | tr ':' '\n' | awk '!x[$0]++' | tr '\n' ' ' )
MLIST=$( echo $MLIST | tr ' ' ':' )
MLIST=$(echo ${MLIST%:}) # To weed out a trailing ':' which was reported once.

# Back to the interactive part:

if [ "$INTERACTIVE" = "1" ];  then
  MLIST=$( dialog --stdout --title "INITRD'S MODULE LIST" --inputbox "\
The list here shows all modules needed to support your root filesystem \
and boot from it.  But you can change the list to use some alternative \
or additional modules.  If you don't know what to do, the default is safe." \
0 0 "$MLIST" )
  if [ "$?" != "0" ]; then
    exit 1
  fi

  EXTRA=$( dialog --stdout --title "EXTRA CONFIGURATION" --checklist "\
Now is your chance for some additional configuration.  All of these \
configurations are optional and you can stick to the defaults." 11 72 3 \
"KEYMAP" "Select keyboard layout (default: US)" \
			   $([ $USING_LUKS = 1 ] && echo on || echo off) \
"RESUMEDEV" "Select device for 'suspend-to-disk' feature" off \
"UDEV" "Use UDEV in the initrd for device configuration" $(test $UDEV -eq 1 && echo on || echo off) \
"WAIT" "Add delay to allow detection of slow disks at boot" $(test $WAIT -gt $WAIT_DEFAULT && echo on || echo off) )
  if [ "$?" != "0" ]; then
    exit 1
  fi

  if echo $EXTRA | grep -q KEYMAP ; then
    KEYMAP=$( dialog --stdout --title "KEYBOARD LAYOUT SELECTION" \
      --cancel-label "Skip" \
      --menu "You may select one of the following keyboard layouts. \
If you do not select a keyboard map, 'us.map' \
(the US keyboard layout) is the default.  Use the UP/DOWN \
arrow keys and PageUp/PageDown to scroll \
through the whole list of choices." \
22 55 11 \
"qwerty/us.map" "" \
"azerty/azerty.map" "" \
"azerty/be-latin1.map" "" \
"azerty/fr-latin0.map" "" \
"azerty/fr-latin1.map" "" \
"azerty/fr-latin9.map" "" \
"azerty/fr-old.map" "" \
"azerty/fr-pc.map" "" \
"azerty/fr.map" "" \
"azerty/wangbe.map" "" \
"azerty/wangbe2.map" "" \
"dvorak/ANSI-dvorak.map" "" \
"dvorak/dvorak-l.map" "" \
"dvorak/dvorak-r.map" "" \
"dvorak/dvorak.map" "" \
"dvorak/no-dvorak.map" "" \
"fgGIod/tr_f-latin5.map" "" \
"fgGIod/trf-fgGIod.map" "" \
"olpc/es-olpc.map" "" \
"olpc/pt-olpc.map" "" \
"qwerty/bg-cp1251.map" "" \
"qwerty/bg-cp855.map" "" \
"qwerty/bg_bds-cp1251.map" "" \
"qwerty/bg_bds-utf8.map" "" \
"qwerty/bg_pho-cp1251.map" "" \
"qwerty/bg_pho-utf8.map" "" \
"qwerty/br-abnt.map" "" \
"qwerty/br-abnt2.map" "" \
"qwerty/br-latin1-abnt2.map" "" \
"qwerty/br-latin1-us.map" "" \
"qwerty/by-cp1251.map" "" \
"qwerty/by.map" "" \
"qwerty/bywin-cp1251.map" "" \
"qwerty/cf.map" "" \
"qwerty/cz-cp1250.map" "" \
"qwerty/cz-lat2-prog.map" "" \
"qwerty/cz-lat2.map" "" \
"qwerty/cz-qwerty.map" "" \
"qwerty/defkeymap.map" "" \
"qwerty/defkeymap_V1.0.map" "" \
"qwerty/dk-latin1.map" "" \
"qwerty/dk.map" "" \
"qwerty/emacs.map" "" \
"qwerty/emacs2.map" "" \
"qwerty/es-cp850.map" "" \
"qwerty/es.map" "" \
"qwerty/et-nodeadkeys.map" "" \
"qwerty/et.map" "" \
"qwerty/fi-latin1.map" "" \
"qwerty/fi-latin9.map" "" \
"qwerty/fi-old.map" "" \
"qwerty/fi.map" "" \
"qwerty/gr-pc.map" "" \
"qwerty/gr.map" "" \
"qwerty/hu101.map" "" \
"qwerty/il-heb.map" "" \
"qwerty/il-phonetic.map" "" \
"qwerty/il.map" "" \
"qwerty/is-latin1-us.map" "" \
"qwerty/is-latin1.map" "" \
"qwerty/it-ibm.map" "" \
"qwerty/it.map" "" \
"qwerty/it2.map" "" \
"qwerty/jp106.map" "" \
"qwerty/kazakh.map" "" \
"qwerty/kyrgyz.map" "" \
"qwerty/la-latin1.map" "" \
"qwerty/lt.baltic.map" "" \
"qwerty/lt.l4.map" "" \
"qwerty/lt.map" "" \
"qwerty/mk-cp1251.map" "" \
"qwerty/mk-utf.map" "" \
"qwerty/mk.map" "" \
"qwerty/mk0.map" "" \
"qwerty/nl.map" "" \
"qwerty/nl2.map" "" \
"qwerty/no-latin1.map" "" \
"qwerty/no.map" "" \
"qwerty/pc110.map" "" \
"qwerty/pl.map" "" \
"qwerty/pl1.map" "" \
"qwerty/pl2.map" "" \
"qwerty/pl3.map" "" \
"qwerty/pl4.map" "" \
"qwerty/pt-latin1.map" "" \
"qwerty/pt-latin9.map" "" \
"qwerty/pt.map" "" \
"qwerty/ro.map" "" \
"qwerty/ro_std.map" "" \
"qwerty/ru-cp1251.map" "" \
"qwerty/ru-ms.map" "" \
"qwerty/ru-yawerty.map" "" \
"qwerty/ru.map" "" \
"qwerty/ru1.map" "" \
"qwerty/ru2.map" "" \
"qwerty/ru3.map" "" \
"qwerty/ru4.map" "" \
"qwerty/ru_win.map" "" \
"qwerty/ruwin_alt-CP1251.map" "" \
"qwerty/ruwin_alt-KOI8-R.map" "" \
"qwerty/ruwin_alt-UTF-8.map" "" \
"qwerty/ruwin_cplk-CP1251.map" "" \
"qwerty/ruwin_cplk-KOI8-R.map" "" \
"qwerty/ruwin_cplk-UTF-8.map" "" \
"qwerty/ruwin_ct_sh-CP1251.map" "" \
"qwerty/ruwin_ct_sh-KOI8-R.map" "" \
"qwerty/ruwin_ct_sh-UTF-8.map" "" \
"qwerty/ruwin_ctrl-CP1251.map" "" \
"qwerty/ruwin_ctrl-KOI8-R.map" "" \
"qwerty/ruwin_ctrl-UTF-8.map" "" \
"qwerty/se-fi-ir209.map" "" \
"qwerty/se-fi-lat6.map" "" \
"qwerty/se-ir209.map" "" \
"qwerty/se-lat6.map" "" \
"qwerty/se-latin1.map" "" \
"qwerty/sk-prog-qwerty.map" "" \
"qwerty/sk-qwerty.map" "" \
"qwerty/speakup-jfw.map" "" \
"qwerty/speakupmap.map" "" \
"qwerty/sr-cy.map" "" \
"qwerty/sv-latin1.map" "" \
"qwerty/tr_q-latin5.map" "" \
"qwerty/tralt.map" "" \
"qwerty/trf.map" "" \
"qwerty/trq.map" "" \
"qwerty/ttwin_alt-UTF-8.map.gz" "" \
"qwerty/ttwin_cplk-UTF-8.map.gz" "" \
"qwerty/ttwin_ct_sh-UTF-8.map.gz" "" \
"qwerty/ttwin_ctrl-UTF-8.map.gz" "" \
"qwerty/ua-cp1251.map.gz" "" \
"qwerty/ua-utf-ws.map" "" \
"qwerty/ua-utf.map" "" \
"qwerty/ua-ws.map" "" \
"qwerty/ua.map" "" \
"qwerty/uk.map" "" \
"qwerty/us-acentos.map" "" \
"qwerty/us.map" "" \
"qwertz/croat.map" "" \
"qwertz/cz-us-qwertz.map" "" \
"qwertz/cz.map" "" \
"qwertz/de-latin1-nodeadkeys.map" "" \
"qwertz/de-latin1.map" "" \
"qwertz/de.map" "" \
"qwertz/de_CH-latin1.map" "" \
"qwertz/fr_CH-latin1.map" "" \
"qwertz/fr_CH.map" "" \
"qwertz/hu.map" "" \
"qwertz/sg-latin1-lk450.map" "" \
"qwertz/sg-latin1.map" "" \
"qwertz/sg.map" "" \
"qwertz/sk-prog-qwertz.map" "" \
"qwertz/sk-qwertz.map" "" \
"qwertz/slovene.map" "" )
    [ -n "$KEYMAP" ] && KEYMAP=$(basename $KEYMAP .map)
  fi

  if echo $EXTRA | grep -q UDEV ; then
    UDEV=1
  fi

  if echo $EXTRA | grep -q RESUMEDEV ; then
    # Print information about swap partitions:
    FREERAM=$(free -k | grep "^Mem:" | tr -s ' ' | cut -d' ' -f2)
    SWPINFO=""
    for SWPDEV in $(grep -w swap $FSTAB | cut -d' ' -f1) ; do
      SWPINFO="$SWPINFO  $SWPDEV    Linux swap partition    $(fdisk -s $SWPDEV) KB \\n"
      [ $(fdisk -s $SWPDEV) -gt $FREERAM ] && RESUMEDEV=$SWPDEV
    done
    FREERAM=$(free -m | grep "^Mem:" | tr -s ' ' | cut -d' ' -f2)
    RESUMEDEV=$( dialog --stdout --no-collapse --title "HIBERNATE RESUME DEVICE" --inputbox "\
When using suspend-to-disk feature (hibernate), your computer's RAM is copied \
to a swap device when it shuts down.  The kernel will resume from that RAM \
image at boot.  This means that the swap partition must not be smaller than \
the amount of RAM you have ($FREERAM MB). \n\
$SWPINFO \n\
Please specify a swap partition to be used for hibernation:" \
0 0 "$RESUMEDEV")
    [ -z "$RESUMEDEV" ] && exit 1
  fi

  if echo $EXTRA | grep -q WAIT ; then
    WAIT=$( dialog --stdout --title "WAIT FOR ROOT DEVICE" --inputbox "\
Some block devices are too slow to be detected properly at boot. USB storage \
devices and some disk arrays have this 'feature'. To make your machine \
boot properly, you can add some delay here, to wait until all your disks are \
probed and detected. The time is in seconds:" 0 0 "$WAIT")
    [ -z "$WAIT" ] && exit 1
  fi
  
  IMGFILE=$( dialog --stdout --title "INITRD IMAGE NAME" --inputbox "\
Enter your initrd image filename." 0 0 "$IMGFILE" )
  [ -z "$IMGFILE" ] && exit 1

else    
  MLIST=$(add_rootfs_module)
fi

# Add any modules passed along on the commandline:
if [ -n "$MKINIT_MODS" ]; then
  [ -n "$MLIST" ] && MLIST="$MLIST:$(echo $MKINIT_MODS | tr ' ' ':')" \
                  || MLIST="$(echo $MKINIT_MODS | tr ' ' ':')"
fi

# Constructing the mkinitrd command:
MKINIT="mkinitrd -c -k $KVER -f $ROOTFS -r $ROOTDEV"

# If we have a module list, add them:
if ! [ -z "$MLIST" -o "$MLIST" = ":" ]; then
  MKINIT="$MKINIT -m $MLIST"
fi

# Deal with LUKS/LVM/RAID:
if [ "$USING_LUKS" = "1" ]; then
  MKINIT="$MKINIT -C $REALDEV"
fi
if [ "$USING_LVM" = "1" ]; then
  MKINIT="$MKINIT -L"
fi
if [ "$USING_RAID" = "1" ]; then
  MKINIT="$MKINIT -R"
fi

if [ -n "$RESUMEDEV" ]; then
  # Add hibernation partition:
  MKINIT="$MKINIT -h $RESUMEDEV"
fi
if [ -n "$KEYMAP" -a "$KEYMAP" != "us" ]; then
  # Add non-us keyboard mapping:
  MKINIT="$MKINIT -l $KEYMAP"
fi
if [ $UDEV -eq 1 ]; then
  # Add UDEV support:
  MKINIT="$MKINIT -u"
fi
if [ -n "$WAIT" -a $WAIT -ne $WAIT_DEFAULT ]; then
  # Add non-default wait time:
  MKINIT="$MKINIT -w $WAIT"
fi
if ! echo "$MKINIT_PARAMS" | grep -q -- '-o ' ; then
  # Add default output filename:
  MKINIT="$MKINIT -o $IMGFILE"
fi
if [ -n "$MKINIT_PARAMS" ]; then
  # Add user-supplied additional parameters:
  MKINIT="$MKINIT $MKINIT_PARAMS"
fi

# Notify the user:
if [ "$EMIT" = "all" ]; then
  cat <<-EOT
	#
	# $(basename $0) revision $REV
	#
	# This script will now make a recommendation about the command to use
	# in case you require an initrd image to boot a kernel that does not
	# have support for your storage or root filesystem built in
	# (such as the Slackware 'generic' kernels').
	# A suitable 'mkinitrd' command will be:
	
	$MKINIT
	EOT
elif [ "$EMIT" = "run" ]; then
  echo "$MKINIT"
elif [ "$EMIT" = "conf" ]; then
  cat <<-EOT
	SOURCE_TREE="$SOURCE_TREE"
	CLEAR_TREE="$CLEAR_TREE"
	OUTPUT_IMAGE="$IMGFILE"
	KERNEL_VERSION="$KVER"
	KEYMAP="$KEYMAP"
	MODULE_LIST="$(echo $MLIST | cut -f2 -d\ )"
	LUKSDEV="$REALDEV"
	ROOTDEV="$ROOTDEV"
	ROOTFS="$ROOTFS"
	RESUMEDEV="$RESUMEDEV"
	RAID="$USING_RAID"
	LVM="$USING_LVM"
	UDEV="$UDEV"
	WAIT="$WAIT"
	EOT
fi

if [ -n "$KFILE" ]; then
  if [ "$EMIT" = "all" ]; then
    cat <<-EOT
	# An entry in 'etc/lilo.conf' for kernel '$KFILE' would look like this:
	EOT
  fi
  if  [ "$EMIT" = "all" -o "$EMIT" = "lilo" ]; then
  # Compensate for the syntax used for the LUKS-on-LVM case:
  [ "$(basename $ROOTDEV)" = "$ROOTDEV" ] && BASE="/dev/mapper/" || BASE=""
    cat <<-EOT
	# Linux bootable partition config begins
	# initrd created with '$MKINIT'
	image = $KFILE
	  initrd = $IMGFILE
	  root = $BASE$ROOTDEV
	  label = $KVER
	  read-only
	# Linux bootable partition config ends
	EOT
  fi
fi
```

</div></details>

</div></details>

## 再起動

```
# reboot
```

※ インストールメディアを取り出し忘れないこと。

参考文献：
https://linuxjm.osdn.jp/html/0MultiFileIdx/man8/reboot.8.html

## / に容量を継ぎ足す場合
コマンド実行例：

```
# fdisk -l
# pvcreate /dev/moomoo
# lvs
# vgextend SlackVG /dev/moomoo
# lvextend -l +100%FREE SlackVG/root
# resize2fs /dev/SlackVG/root
# lvs
```

※ 100%FREE って[何だか体に良さそう](https://duckduckgo.com/?q=気のせい)。
※ /dev/moomoo SlackVG root はハードウェア構成や LVM の設定に応じて適宜読み替えてください。
※ ファイルシステムのオンラインリサイズについて、
　・ext2/3/4 の場合は ```resize2fs``` 。
　・btrfs の場合は ```btrfs filesystem resize``` 。
　・reiserfs の場合は ```resize_reiserfs``` 。
　・xfs の場合は ```xfs_growfs``` 。

<details><summary>参考文献：</summary><div>

Man page of FDISK
https://linuxjm.osdn.jp/html/util-linux/man8/fdisk.8.html

pvcreate(8) - Linux man page
https://linux.die.net/man/8/pvcreate

lvs(8) - Linux man page
https://linux.die.net/man/8/lvs

vgextend(8) - Linux man page
https://linux.die.net/man/8/vgextend

lvextend(8) - Linux man page
https://linux.die.net/man/8/lvextend

resize2fs(8) - Linux man page
https://linux.die.net/man/8/resize2fs

btrfs(8) - Linux man page
https://linux.die.net/man/8/btrfs

Man page of RESIZE_REISERFS
https://linuxjm.osdn.jp/html/reiserfsprogs/man8/resize_reiserfs.8.html

xfs_growfs(8) - Linux man page
https://linux.die.net/man/8/xfs_growfs

LVM - ArchWiki
https://wiki.archlinux.jp/index.php/LVM

Pantora Networks » 3章 LVM操作 応用編
http://pantora.net/pages/linux/lvm/3/

LVMで新たにディスクを追加して既存のLVを拡張する - Qiita
https://qiita.com/g_maeda/items/3602dc2f7af3f711f9c4

LVM上のbtrfsのRAID1のサイズを拡張する - Qiita
https://qiita.com/takaki@github/items/5d96d80dc78b09ea600b

</div></details>

# 備考
## シャットダウン・リブート時の “lvm can't deactivate …” エラー

![VirtualBox_Slackware64_10_07_2019_15_32_53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/410379/f6b81cd7-fe08-5737-46f3-7efebe3bc2ae.png)

LVM に置いたルートでは毎回このエラーが出ますが、リードオンリーで再マウントした後の話なので最悪でもデータが飛ぶ恐れはないそうです。

```
# This never hurts again (especially since root-on-LVM always fails
# to deactivate the / logical volume...  but at least it was
# remounted as read-only first)
/bin/sync
```

出典：

<details><summary>/etc/rc.d/rc.6 本体</summary><div>

```sh:/etc/rc.d/rc.6
#! /bin/sh
#
# rc.6		This file is executed by init when it goes into runlevel
#		0 (halt) or runlevel 6 (reboot). It kills all processes,
#		unmounts file systems and then either halts or reboots.
#
# Version:	@(#)/etc/rc.d/rc.6	2.47 Sat Jan 13 13:37:26 PST 2001
#
# Author:	Miquel van Smoorenburg
# Modified by:  Patrick J. Volkerding
#

# Set the path.
PATH=/sbin:/etc:/bin:/usr/bin

# If there are SystemV init scripts for this runlevel, run them.
if [ -x /etc/rc.d/rc.sysvinit ]; then
  . /etc/rc.d/rc.sysvinit
fi

# Set linefeed mode to avoid staircase effect.
/bin/stty onlcr

echo "Running shutdown script $0:"

# Find out how we were called.
case "$0" in
	*0)
		shutdown_command="halt"
		;;
	*6)
		shutdown_command=reboot
		;;
	*)
		echo "$0: call me as \"rc.0\" or \"rc.6\" please!"
		exit 1
		;;
esac

# Save the system time to the hardware clock using hwclock --systohc.
# This will also create or update the timestamps in /etc/adjtime.
if [ -x /sbin/hwclock ]; then
  # Check for a broken motherboard RTC clock (where ioports for rtc are
  # unknown) to prevent hwclock causing a hang:
  if ! grep -q " : rtc" /proc/ioports ; then
    CLOCK_OPT="--directisa"
  fi
  if [ /etc/adjtime -nt /etc/hardwareclock ]; then
    if grep -q "^LOCAL" /etc/adjtime ; then
      echo "Saving system time to the hardware clock (localtime)."
    else
      echo "Saving system time to the hardware clock (UTC)."
    fi
    /sbin/hwclock $CLOCK_OPT --systohc
  elif grep -q "^UTC" /etc/hardwareclock 2> /dev/null ; then
    echo "Saving system time to the hardware clock (UTC)."
    if [ ! -r /etc/adjtime ]; then
      echo "Creating system time correction file /etc/adjtime."
    fi
    /sbin/hwclock $CLOCK_OPT --utc --systohc
  else
    echo "Saving system time to the hardware clock (localtime)."
    if [ ! -r /etc/adjtime ]; then
      echo "Creating system time correction file /etc/adjtime."
    fi
    /sbin/hwclock $CLOCK_OPT --localtime --systohc
  fi
fi

# Run any local shutdown scripts:
if [ -x /etc/rc.d/rc.local_shutdown ]; then
  /etc/rc.d/rc.local_shutdown stop
fi

# Stop the Apache web server:
if [ -x /etc/rc.d/rc.httpd ]; then
  /etc/rc.d/rc.httpd stop
fi

# Stop the MySQL database:
if [ -r /var/run/mysql/mysql.pid ]; then
  . /etc/rc.d/rc.mysqld stop
fi

# Stop the Samba server:
if [ -x /etc/rc.d/rc.samba ]; then
  . /etc/rc.d/rc.samba stop
fi

# Shut down the NFS server:
if [ -x /etc/rc.d/rc.nfsd ]; then
  /etc/rc.d/rc.nfsd stop
fi

# Shut down the SSH server:
if [ -x /etc/rc.d/rc.sshd ]; then
  /etc/rc.d/rc.sshd stop
fi

# Shut down the SASL authentication daemon:
if [ -x /etc/rc.d/rc.saslauthd ]; then
  /etc/rc.d/rc.saslauthd stop
fi

# Shut down OpenLDAP:
if [ -x /etc/rc.d/rc.openldap ]; then
  /etc/rc.d/rc.openldap stop
fi

# Stop D-Bus:
if [ -x /etc/rc.d/rc.messagebus ]; then
  sh /etc/rc.d/rc.messagebus stop
fi

# Kill any processes (typically gam) that would otherwise prevent
# unmounting NFS volumes:
unset FUSER_DELAY
for dir in $(/bin/mount | grep 'type nfs ' | cut -d ' ' -f 3 ) ; do
  echo "Killing processes holding NFS mount $dir open..."
  # Background this to prevent fuser from also blocking shutdown:
  /usr/bin/fuser -k -m $dir &
  FUSER_DELAY=5
done
# If fuser was run, let it have some delay:
if [ ! -z "$FUSER_DELAY" ]; then
  sleep $FUSER_DELAY
fi

# Unmount any NFS, SMB, or CIFS filesystems:
echo "Unmounting remote filesystems:"
/bin/umount -v -a -l -f -r -t nfs,smbfs,cifs | tr -d ' ' | grep successfully | sed "s/:successfullyunmounted/ has been successfully unmounted./g"

# Try to shut down pppd:
PS="$(ps ax)"
if echo "$PS" | /bin/grep -q -w pppd ; then
  if [ -x /usr/sbin/ppp-off ]; then
    /usr/sbin/ppp-off
  fi
fi

# Shut down YP services:
if [ -x /etc/rc.d/rc.yp ]; then
  if grep -wq stop /etc/rc.d/rc.yp ; then
    /etc/rc.d/rc.yp stop
  fi
fi

# Bring down the networking system, but first make sure that this
# isn't a diskless client with the / partition mounted via NFS:
if ! /bin/mount | /bin/grep -q 'on / type nfs' ; then
  if [ -x /etc/rc.d/rc.inet1 ]; then
    . /etc/rc.d/rc.inet1 stop
  fi
fi

# In case dhcpcd might have been manually started on the command line,
# look for the .pid file, and shut dhcpcd down if it's found:
if /bin/ls /etc/dhcpc/*.pid 1> /dev/null 2> /dev/null ; then
  /sbin/dhcpcd -k 1> /dev/null 2> /dev/null
  # A little time for /etc/resolv.conf and/or other files to
  # restore themselves.
  sleep 2
fi

# Shut down PCMCIA devices:
if [ -x /etc/rc.d/rc.pcmcia ]; then
  . /etc/rc.d/rc.pcmcia stop
  # The cards might need a little extra time here to deactivate:
  /bin/sleep 5
fi

# Turn off process accounting:
if [ -x /sbin/accton -a -r /var/log/pacct ]; then
  /sbin/accton off
fi

# Terminate acpid before syslog:
if [ -x /etc/rc.d/rc.acpid -a -r /var/run/acpid.pid ]; then # quit
  . /etc/rc.d/rc.acpid stop
fi

# Stop udev:
if [ -x /etc/rc.d/rc.udev ]; then
  sh /etc/rc.d/rc.udev force-stop
fi

# Kill all remaining processes.
OMITPIDS="$(for p in $(pgrep mdmon); do echo -o $p; done)" # Don't kill mdmon
if [ ! "$1" = "fast" ]; then
  echo "Sending all processes the SIGTERM signal."
  /sbin/killall5 -15 $OMITPIDS
  /bin/sleep 5
  echo "Sending all processes the SIGKILL signal."
  /sbin/killall5 -9 $OMITPIDS
fi

# Try to turn off quota.
if /bin/grep -q quota /etc/fstab ; then
  if [ -x /sbin/quotaoff ]; then
    echo "Turning off filesystem quotas."
    /sbin/quotaoff -a
  fi
fi

# Carry a random seed between reboots.
echo "Saving random seed from /dev/urandom in /etc/random-seed."
# Use the pool size from /proc, or 4096 bits:
if [ -r /proc/sys/kernel/random/poolsize ]; then
  /bin/dd if=/dev/urandom of=/etc/random-seed count=1 bs=$(expr $(cat /proc/sys/kernel/random/poolsize) / 8) 2> /dev/null
else
  /bin/dd if=/dev/urandom of=/etc/random-seed count=1 bs=512 2> /dev/null
fi
/bin/chmod 600 /etc/random-seed

# Before unmounting file systems write a reboot or halt record to wtmp.
$shutdown_command -w

# Turn off swap:
echo "Turning off swap."
/sbin/swapoff -a
/bin/sync

# Stop cgmanager and cgproxy:
if [ -x /etc/rc.d/rc.cgmanager ]; then
  sh /etc/rc.d/rc.cgmanager stop
fi

echo "Unmounting local file systems:"
/bin/umount -v -a -t no,proc,sysfs | tr -d ' ' | grep successfully | sed "s/:successfullyunmounted/ has been successfully unmounted./g" 2> /dev/null

echo "Remounting root filesystem read-only:"
/bin/mount -v -n -o remount,ro /

# This never hurts:
/bin/sync

# Close any volumes opened by cryptsetup:
if [ -f /etc/crypttab -a -x /sbin/cryptsetup ]; then
  cat /etc/crypttab | grep -v "^#" | grep -v "^$" | while read line; do
    # NOTE: we only support LUKS formatted volumes (except for swap)!
    LUKS=$(echo $line | tr '\t' ' ' | tr -s ' ' | cut -f1 -d' ')
    DEV=$(echo $line | tr '\t' ' ' | tr -s ' ' | cut -f2 -d' ')
    OPTS=$(echo $line | tr '\t' ' ' | tr -s ' ' | cut -f4 -d' ')
    if /sbin/cryptsetup isLuks $DEV 2>/dev/null ; then
      echo "Locking LUKS crypt volume '${LUKS}':"
      /sbin/cryptsetup luksClose ${LUKS}
    elif echo $OPTS | grep -wq swap ; then
      # If any of the volumes was used as encrypted swap,
      # then run mkswap on the underlying device -
      # in case other Linux installations on this computer should use it:
      echo "Erasing encrypted swap '${LUKS}' and restoring normal swap on ${DEV}:"
      /sbin/cryptsetup remove ${LUKS}
      mkswap $DEV
    fi
  done
fi

# Deactivate LVM volume groups:
if [ -r /etc/lvmtab -o -d /etc/lvm/backup ]; then
  echo "Deactivating LVM volume groups:"
  /sbin/vgchange -an --ignorelockingfailure
fi

# This never hurts again (especially since root-on-LVM always fails
# to deactivate the / logical volume...  but at least it was
# remounted as read-only first)
/bin/sync

# sleep 3 fixes problems with some hard drives that don't
# otherwise finish syncing before reboot or poweroff
/bin/sleep 3

# This is to ensure all processes have completed on SMP machines:
wait

if [ -x /sbin/genpowerd ]; then
  # See if this is a powerfail situation:
  if /bin/egrep -q "FAIL|SCRAM" /etc/upsstatus 2> /dev/null ; then
    # Signal UPS to shut off the inverter:
    /sbin/genpowerd -k
    if [ ! $? = 0 ]; then
      echo
      echo "There was an error signaling the UPS."
      echo "Perhaps you need to edit /etc/genpowerd.conf to configure"
      echo "the serial line and UPS type."
      # Wasting 15 seconds of precious power:
      /bin/sleep 15
    fi
  fi
fi

# Now halt (poweroff with APM or ACPI enabled kernels) or reboot.
if [ "$shutdown_command" = "reboot" ]; then
  echo "Rebooting."
  /sbin/reboot
else
  /sbin/poweroff
fi
```

</div></details>

## ELILO におけるハイバネート機能（SWSUSP）の有効化、任意のカーネルパラメータの適用
/usr/sbin/eliloconfig を編集することで swsusp に対応させることが出来る他、起動時にお好みのカーネルパラメータを渡すことが可能です。

該当個所を、例えば以下のように編集します。

```sh:/usr/sbin/eliloconfig
cat << EOF >> $T_PX/boot/efi/EFI/Slackware/elilo.conf
        read-only
        append="root=$ROOT_DEVICE vga=normal ro"
EOF
```

```sh:/usr/local/sbin/eliloconfig-modified
cat << EOF >> $T_PX/boot/efi/EFI/Slackware/elilo.conf
        read-only
        append="root=$ROOT_DEVICE resume=/dev/moo2 vga=normal ro"
EOF
```

resume=/dev/moo2 はインストールした Slackware で使用している swap 領域を指定。

直接編集せず、/usr/local/sbin/eliloconfig-modified 等の名前でコピーを作成してそちらを編集すると安全かも。

※ ~~swap 領域が LVM の中にある場合~~ initramfsを使い、かつ swsusp を行う場合は ```mkinitrd``` に ```-h /dev/moo``` 等を渡してやる ~~必要があるようです~~ のが無難です。[^ハイバネ1] [^ハイバネ2]

[^ハイバネ1]: https://docs.slackware.com/howtos:slackware_admin:hibernation

[^ハイバネ2]: https://www.slackwiki.com/LVM/Luks_Encryption

<details><summary>参考文献：</summary><div>

howtos:slackware_admin:hibernation - SlackDocs
https://docs.slackware.com/howtos:slackware_admin:hibernation

Using swap files with software suspend (swsusp)
https://www.kernel.org/doc/Documentation/power/swsusp-and-swap-files.txt

[サスペンドとハイバネート - ArchWiki](https://wiki.archlinux.jp/index.php/%E3%82%B5%E3%82%B9%E3%83%9A%E3%83%B3%E3%83%89%E3%81%A8%E3%83%8F%E3%82%A4%E3%83%90%E3%83%8D%E3%83%BC%E3%83%88)

kernel.org の Documentation/admin-guide/kernel-parameters.txt
https://www.kernel.org/doc/Documentation/admin-guide/kernel-parameters.txt

[カーネルパラメータ - ArchWiki](https://wiki.archlinux.jp/index.php/%E3%82%AB%E3%83%BC%E3%83%8D%E3%83%AB%E3%83%91%E3%83%A9%E3%83%A1%E3%83%BC%E3%82%BF)

Man page of BOOTPARAM
https://linuxjm.osdn.jp/html/LDP_man-pages/man7/bootparam.7.html

</div></details>

### initramfs と swsusp と LVM についての掘り下げ

<details><summary>/boot/initrd.gz 内の init スクリプト</summary><div>


```sh:init
#!/bin/ash
#
# /init:  init script to load kernel modules from an initramfs
#         This requires that your kernel supports initramfs!!!
#
# Copyright 2004  Slackware Linux, Inc., Concord, CA, USA
# Copyright 2007, 2008, 2009, 2010, 2012  Patrick J. Volkerding, Sebeka, MN, USA
# All rights reserved.
#
# Redistribution and use of this script, with or without modification, is
# permitted provided that the following conditions are met:
#
# 1. Redistributions of this script must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
#
#  THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
#  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
#  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO
#  EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
#  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
#  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
#  OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
#  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
#  OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
#  ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
##################################################################################
# With a generic kernel, you need to load the modules needed to mount the
# root partition.  This might mean a SCSI, RAID, or other drive controller
# module, as well as the module to support the root filesystem.  Once the
# root partition is mounted all the other modules will be available so you
# don't need to load them here.
#
# Config files used by this script:
#
# /rootdev        Contains the name of the root device, such as: /dev/hda1 
#
# /rootfs         Contains the root filesystem type, such as: xfs
#
# /initrd-name    Contains the name of the initrd file.
#
# /resumedev      Contains the name of the device to resume from hibernation.
#
# /luksdev        Contains colon separated list of luks encrypted devices to
#                 be unlocked.
#
# /lukstrim       Contains colon separated list of luks encrypted devices to
#                 pass '--allow-discards' when unlocking
#
# /lukskey        Contains the path to a LUKS key-file for automatic unlock
#                 Format: LABEL=<partition_label>:/path/to/file
#                         UUID=<partition_uuid>:/path/to/file
#
# /wait-for-root  Contains a number - the init script will wait this amount
#                 of seconds before creating device nodes.
#
# /keymap         Contains the name for a custom keyboard map 
#
# Optional:
#
# /load_kernel_modules
#                 A script that uses modprobe to load the desired modules.
#
#                 There's an example in here.  To actually use it, you'll
#                 need to make it executable:  
#
#                     chmod 755 load_kernel_modules
##################################################################################
# Changelog
# 10-Dec-2012 mozes
#  * Added support for the official Kernel parameters to select root filesystem
#    type ('rootfstype') and pause before attempting to mount the root filesystem
#    ('rootdelay').  The original parameters may continue to be used.
##################################################################################

INITRD=$(cat /initrd-name)
ROOTDEV=$(cat /rootdev)
ROOTFS=$(cat /rootfs)
LUKSDEV=$(cat /luksdev)
LUKSTRIM=$(cat /lukstrim 2>/dev/null)
LUKSKEY=$(cat /lukskey)
RESUMEDEV=$(cat /resumedev)
WAIT=$(cat /wait-for-root)
KEYMAP=$(cat /keymap)
INIT=/sbin/init

PATH="/sbin:/bin:/usr/sbin:/usr/bin"

# Mount /proc and /sys:
mount -n proc /proc -t proc
mount -n sysfs /sys -t sysfs
mount -n tmpfs /run -t tmpfs -o mode=0755,size=32M,nodev,nosuid,noexec

if grep devtmpfs /proc/filesystems 1>/dev/null 2>/dev/null ; then
  DEVTMPFS=1
  mount -n devtmpfs /dev -t devtmpfs -o size=8M
fi	

# Parse command line
for ARG in $(cat /proc/cmdline); do
  case $ARG in
    0|1|2|3|4|5|6|S|s|single)
      RUNLEVEL=$ARG
    ;;
    init=*)
      INIT=$(echo $ARG | cut -f2 -d=)
    ;;
    luksdev=/dev/*)
      LUKSDEV=$(echo $ARG | cut -f2 -d=)
    ;;
    lukskey=*)
      LUKSKEY=$(echo $ARG | cut -f2- -d=)
    ;;
    rescue)
      RESCUE=1
    ;;
    resume=*)
      RESUMEDEV=$(echo $ARG | cut -f2- -d=)
    ;;
    root=/dev/*)
      ROOTDEV=$(echo $ARG | cut -f2 -d=)
    ;;
    root=LABEL=*)
      ROOTDEV=$(echo $ARG | cut -f2- -d=)
    ;;
    root=UUID=*)
      ROOTDEV=$(echo $ARG | cut -f2- -d=)
    ;;
    rootfs=*|rootfstype=*)
      ROOTFS=$(echo $ARG | cut -f2 -d=)
    ;;
    waitforroot=*|rootdelay=*)
      WAIT=$(echo $ARG | cut -f2 -d=)
    ;;
  esac
done

# If udevd is available, use it to generate block devices
# else use mdev to read sysfs and generate the needed devices 
if [ -x /sbin/udevd -a -x /sbin/udevadm ]; then
  /sbin/udevd --daemon --resolve-names=never
  /sbin/udevadm trigger --subsystem-match=block --action=add
  /sbin/udevadm settle --timeout=10
else
  [ "$DEVTMPFS" != "1" ] && mdev -s
fi

# Load kernel modules (ideally this was already done by udev):
if [ ! -d /lib/modules/$(uname -r) ]; then
  echo "No kernel modules found for Linux $(uname -r)."
elif [ -x ./load_kernel_modules ]; then # use load_kernel_modules script:
  echo "${INITRD}:  Loading kernel modules from initrd image:"
  . ./load_kernel_modules
else # load modules (if any) in order:
  if ls /lib/modules/$(uname -r)/*.*o 1> /dev/null 2> /dev/null ; then
    echo "${INITRD}:  Loading kernel modules from initrd image:"
    for module in /lib/modules/$(uname -r)/*.*o ; do
      /sbin/modprobe $module
    done
    unset module
  fi
fi

# Sometimes the devices need extra time to be available.
# A root filesystem on USB is a good example of that.
sleep $WAIT

# Load a custom keyboard mapping:
if [ -n "$KEYMAP" ]; then
  echo "${INITRD}:  Loading '$KEYMAP' keyboard mapping:"
  tar xzOf /etc/keymaps.tar.gz ${KEYMAP}.bmap | loadkmap
fi

if [ "$RESCUE" = "" ]; then 
  # Initialize RAID:
  if [ -x /sbin/mdadm ]; then
    # If /etc/mdadm.conf is present, udev should DTRT on its own;
    # If not, we'll make one and go from there:
    if [ ! -r /etc/mdadm.conf ]; then
      /sbin/mdadm -E -s >/etc/mdadm.conf
      /sbin/mdadm -S -s
      /sbin/mdadm -A -s
      # This seems to make the kernel see partitions more reliably:
      fdisk -l /dev/md* 1> /dev/null 2> /dev/null
    fi
  fi

  # Unlock any encrypted partitions necessary to access the
  # root filesystem, such as encrypted LVM Physical volumes, disk
  # partitions or mdadm arrays.
  # Unavailable devices such as LVM Logical Volumes will need to be
  # deferred until they become available after the vgscan.

  if [ -x /sbin/cryptsetup ]; then

    # Determine if we have to use a LUKS keyfile:
    if [ ! -z "$LUKSKEY" ]; then
      mkdir  /mountkey
      KEYPART=$(echo $LUKSKEY |cut -f1 -d:)
      KEYNAME=$(echo $KEYPART |cut -f2 -d=)
      LUKSPATH="/mountkey$(echo $LUKSKEY |cut -f2 -d:)"
      # Catch possible mount failure:
      if blkid |grep "TYPE=\"vfat\"" |grep $KEYNAME 1>/dev/null 2>&1 ; then
        MOUNTOPTS="-t vfat -o shortname=mixed"
      else
        MOUNTOPTS="-t auto"
      fi
      mount $MOUNTOPTS $(findfs $KEYPART) /mountkey 2>/dev/null
      # Check if we can actually use this file:
      if [ ! -f $LUKSPATH ]; then
        LUKSKEY=""
      else
        echo ">>> Using LUKS key file: '$LUKSKEY'"
        LUKSKEY="-d $LUKSPATH"
      fi
    fi

    LUKSLIST_DEFERRED=""
    LUKSLIST=$(echo $LUKSDEV | tr -s ':' ' ')
    for LUKSDEV in $LUKSLIST ; do
      if /sbin/cryptsetup isLuks ${LUKSDEV} 1>/dev/null 2>/dev/null ; then
        if echo $ROOTDEV | grep -q "LABEL=" || echo $ROOTDEV | grep -q "UUID=" ; then
          CRYPTDEV="luks$(basename $LUKSDEV)"
        elif [ "x$ROOTDEV" = "x$(basename $ROOTDEV)" ]; then
          CRYPTDEV="$ROOTDEV"
        else
          CRYPTDEV="luks$(basename $LUKSDEV)"
        fi
        if echo $LUKSTRIM | grep -wq $LUKSDEV 2>/dev/null ; then 
          LUKSOPTS="--allow-discards"
        else
          LUKSOPTS=""
        fi
        if [ -z "${LUKSOPTS}" ]; then
          echo "Unlocking LUKS encrypted device '${LUKSDEV}' as luks mapped device '$CRYPTDEV':"
        else
          echo "Unlocking LUKS encrypted device '${LUKSDEV}' as luks mapped device '$CRYPTDEV' with '$LUKSOPTS':"
        fi
        /sbin/cryptsetup ${LUKSOPTS} ${LUKSKEY} luksOpen ${LUKSDEV} ${CRYPTDEV} </dev/tty0 >/dev/tty0 2>&1
        if [ "$ROOTDEV" = "$LUKSDEV" -o "$ROOTDEV" = "$CRYPTDEV" ] ; then
          ROOTDEV="/dev/mapper/$CRYPTDEV"
        fi
      else
        LUKSLIST_DEFERRED="${LUKSLIST_DEFERRED} ${LUKSDEV}"
      fi
    done
  fi

  # Initialize LVM:
  if [ -x /sbin/vgchange ]; then
    mkdir -p /var/lock/lvm	# this avoids useless warnings
    /sbin/vgchange -ay --ignorelockingfailure 2>/dev/null
    /sbin/udevadm settle --timeout=10
  fi
  
  # Unlock any LUKS encrypted devices that were deferred above but have now
  # become available due to the vgscan (i.e. filesystems on LVM Logical Volumes)

  if [ -x /sbin/cryptsetup -a -n "${LUKSLIST_DEFERRED}" ]; then
    for LUKSDEV in ${LUKSLIST_DEFERRED} ; do
      if /sbin/cryptsetup isLuks ${LUKSDEV} 1>/dev/null 2>/dev/null ; then
        if echo $ROOTDEV | grep -q "LABEL=" || echo $ROOTDEV | grep -q "UUID=" ; then
          CRYPTDEV="luks$(basename $LUKSDEV)"
        elif [ "x$ROOTDEV" = "x$(basename $ROOTDEV)" ]; then
          CRYPTDEV="$ROOTDEV"
        else
          CRYPTDEV="luks$(basename $LUKSDEV)"
        fi
        echo "Unlocking LUKS encrypted device '${LUKSDEV}' as luks mapped device '$CRYPTDEV':"
        /sbin/cryptsetup ${LUKSKEY} luksOpen ${LUKSDEV} ${CRYPTDEV} </dev/tty0 >/dev/tty0 2>&1
        if [ "$ROOTDEV" = "$LUKSDEV" -o "$ROOTDEV" = "$CRYPTDEV" ] ; then
          ROOTDEV="/dev/mapper/$CRYPTDEV"
        fi
      else
        echo "LUKS device '${LUKSDEV}' unavailable for unlocking!"
      fi
    done
    /sbin/udevadm settle --timeout=10
  fi

  # Scan for btrfs multi-device filesystems:
  if [ -x /sbin/btrfs ]; then
    /sbin/btrfs device scan
  fi
  
  # Find root device if a label or UUID was given:
  if echo $ROOTDEV | grep -q "LABEL=" || \
     echo $ROOTDEV | grep -q "UUID=" ; then
    ROOTDEV=$(findfs $ROOTDEV)
  fi

  # Clean up after LUKS unlock using a keyfile:
  if grep -q mountkey /proc/mounts 2>/dev/null ; then
    umount -l /mountkey
    rmdir /mountkey 2>/dev/null
  fi
  
  # Resume state from swap
  if [ "$RESUMEDEV" != "" ]; then
    # Find resume device if a label or UUID was given:
    if echo $RESUMEDEV | grep -q "LABEL=" || \
       echo $RESUMEDEV | grep -q "UUID=" ; then
      RESUMEDEV=$(findfs $RESUMEDEV)
    elif ls -l $RESUMEDEV | grep -q "^l" ; then
      RESUMEDEV=$(readlink -f $RESUMEDEV)
    fi
    echo "Trying to resume from $RESUMEDEV"
    RESMAJMIN=$(ls -l $RESUMEDEV | tr , : | awk '{ print $5$6 }')
    echo $RESMAJMIN > /sys/power/resume
  fi

  # Switch to real root partition:
  /sbin/udevadm settle --timeout=10
  echo 0x0100 > /proc/sys/kernel/real-root-dev
  mount -o ro -t $ROOTFS $ROOTDEV /mnt
  
  if [ ! -r /mnt/sbin/init ]; then
    echo "ERROR:  No /sbin/init found on rootdev (or not mounted).  Trouble ahead."
    echo "        You can try to fix it. Type 'exit' when things are done." 
    echo
    /bin/sh
  fi
else
  echo
  echo "RESCUE mode"
  echo
  echo "        You can try to fix or rescue your system now. If you want"
  echo "        to boot into your fixed system, mount your root filesystem"
  echo "        read-only under /mnt:"
  echo
  echo "            # mount -o ro -t filesystem root_device /mnt"
  echo
  echo "        Type 'exit' when things are done."
  echo
  /bin/sh
fi

# Need to make sure OPTIONS+="db_persist" exists for all dm devices
# That should be handled in /sbin/mkinitrd now
/sbin/udevadm info --cleanup-db
/sbin/udevadm control --exit

unset ERR
mount -o move /proc /mnt/proc
mount -o move /sys /mnt/sys
mount -o move /run /mnt/run

[ "$DEVTMPFS" = "1" ] && mount -o move /dev /mnt/dev
echo "${INITRD}:  exiting"
exec switch_root /mnt $INIT $RUNLEVEL
```

</div></details>

**考察：**
LVM に "/" をいれた場合、ramdisk（initrd.gz）無しだと "/" をマウント出来ずカーネルパニックに。ramdisk を生成させると上記で引用した init スクリプトが LVM や LUKS 及び RAID を上手く扱ってくれる。

また、Slackware の mkinitrd に -h オプションで swap デバイスのパス渡すと initrd.gz 内の resumedev 内に swsusp 対象デバイスのパスが書き込まれる。

ramdisk の init は最後に "/" をリードオンリーでマウントし、"/" 内部の sbin/init を呼び出すが、mkinitrd に -h オプションを渡しておくことで、ramdisk の段階でレジュームしてくれる。

**結論：**
Slackware でブート時に ramdisk（initrd.gz）を使う場合は -h オプションでサスペンド／ レジュームのための swap 領域を指定しておくと、実ドライブ側の init に移ってからレジュームプロセスを始めるより早い段階でレジューム出来、時間短縮になると予想する。また、レジュームが実ドライブマウント前に行われるので問題も生じにくいと思われる。

**懸念：**
EFI システムパーティション上の initrd.gz を第三者に改竄されるとヤバい（ESP がいじれる段階まで侵入されている時点で既にアウトではあるけれども）。

<details><summary>参考文献：</summary><div>

howtos:slackware_admin:hibernation - SlackDocs
https://docs.slackware.com/howtos:slackware_admin:hibernation

１８：initrdファイルの作成 | 日経 xTECH（クロステック）
https://tech.nikkeibp.co.jp/it/article/COLUMN/20061025/251702/

Linux 初期 RAM ディスク (initrd) の概要
https://www.ibm.com/developerworks/jp/linux/library/l-initrd/index.html

</div></details>

# 編集後記：
主要な設定・管理ツールがすべてシェルスクリプト[^configtool]というのは見通しが良いし、改造するにも楽しいので助かります。怪しげなツールを自作する際の教材としても Good!

何だこのエラー？と思ってスクリプトを開くとコメント行で解説がついているのはとても助かりました。

Slackware、使って楽しいディストリです。

[^configtool]: 例えば pkgtool 関係は軒並みシェルスクリプト（＋ dialog コマンド）。