0%

UbuntuOSベースのシンクライアントを作成していた時、
起動時のスプラッシュロゴ表示や、ファイルシステムの復号化処理を行うため、
initrd内部の書き換えを行っていました

なかなかプライベートで触る事は少ないため、書き残しておきます。

環境

  • Linux Mint 19

initrdとは

Linuxを起動する際に、各ディストリビューションとして動作するために必要なサービスやドライバが含まれたファイル群を固めた物

Linuxが起動する時には、基本的に以下の順序で読み込まれていく

  • 1.UEFI
  • 2.ブートローダ
  • 3.カーネル
  • 4.init

カーネルを読み込み基本的なコマンドを使用できる状態にしてから、initrdをマウント、initrd内部の/initを実行する

Linux起動時に黒画面に出現するログは、大半がinitを実行している時に出ている

手順

initrdを作業ディレクトリにコピーする

/bootフォルダ以下に似たような名前のファイルがあるため、それをコピーしてください

1
2
3
$ mkdir work # 作業フォルダ作成
$ cd work
$ cp /boot/initrd.img-4.15.0-54-generic ./initrd

zcatで復号しつつ、ファイルとして出力

1
2
3
$ mkdir initrdfiles
$ cd initrdfiles
$ zcat ../initrd | cpio -idmv

ファイルを確認

1
2
$ ls
bin conf etc init lib lib64 run sbin scripts usr var

initを見てみる

1
$ vi init

詳しくは触れませんが、shによって実行できる形で書かれている事がわかります。

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
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
#!/bin/sh

# Default PATH differs between shells, and is not automatically exported
# by klibc dash. Make it consistent.
export PATH=/sbin:/usr/sbin:/bin:/usr/bin

[ -d /dev ] || mkdir -m 0755 /dev
[ -d /root ] || mkdir -m 0700 /root
[ -d /sys ] || mkdir /sys
[ -d /proc ] || mkdir /proc
[ -d /tmp ] || mkdir /tmp
mkdir -p /var/lock
mount -t sysfs -o nodev,noexec,nosuid sysfs /sys
mount -t proc -o nodev,noexec,nosuid proc /proc

case " $(cat /proc/cmdline) " in
*\ quiet\ *)
quiet=y
;;
*)
quiet=n
echo "Loading, please wait..."
;;
esac
export quiet

# Note that this only becomes /dev on the real filesystem if udev's scripts
# are used; which they will be, but it's worth pointing out
mount -t devtmpfs -o nosuid,mode=0755 udev /dev
mkdir /dev/pts
mount -t devpts -o noexec,nosuid,gid=5,mode=0620 devpts /dev/pts || true
mount -t tmpfs -o "noexec,nosuid,size=10%,mode=0755" tmpfs /run
mkdir -m 0755 /run/initramfs

# Export the dpkg architecture
export DPKG_ARCH=
. /conf/arch.conf

# Set modprobe env
export MODPROBE_OPTIONS="-qb"

# Export relevant variables
export ROOT=
export ROOTDELAY=
export ROOTFLAGS=
export ROOTFSTYPE=
export IP=
export IP6=
export BOOT=
export BOOTIF=
export UBIMTD=
export break=
export init=/sbin/init
export readonly=y
export rootmnt=/root
export debug=
export panic=
export blacklist=
export resume=
export resume_offset=
export drop_caps=
export fastboot=n
export forcefsck=n
export fsckfix=
export recovery=
export NETWORK_SKIP_ENSLAVED=


# mdadm needs hostname to be set. This has to be done before the udev rules are called!
if [ -f "/etc/hostname" ]; then
/bin/hostname -F /etc/hostname >/dev/null 2>&1
fi

# Bring in the main config
. /conf/initramfs.conf
for conf in conf/conf.d/*; do
[ -f ${conf} ] && . ${conf}
done
. /scripts/functions

# Parse command line options
for x in $(cat /proc/cmdline); do
case $x in
init=*)
init=${x#init=}
;;
root=*)
ROOT=${x#root=}
if [ -z "${BOOT}" ] && [ "$ROOT" = "/dev/nfs" ]; then
BOOT=nfs
fi
;;
rootflags=*)
ROOTFLAGS="-o ${x#rootflags=}"
;;
rootfstype=*)
ROOTFSTYPE="${x#rootfstype=}"
;;
rootdelay=*)
ROOTDELAY="${x#rootdelay=}"
case ${ROOTDELAY} in
*[![:digit:].]*)
ROOTDELAY=
;;
esac
;;
resumedelay=*)
RESUMEDELAY="${x#resumedelay=}"
;;
loop=*)
LOOP="${x#loop=}"
;;
loopflags=*)
LOOPFLAGS="-o ${x#loopflags=}"
;;
loopfstype=*)
LOOPFSTYPE="${x#loopfstype=}"
;;
cryptopts=*)
cryptopts="${x#cryptopts=}"
;;
nfsroot=*)
NFSROOT="${x#nfsroot=}"
;;
netboot=*)
NETBOOT="${x#netboot=}"
;;
ip=*)
IP="${x#ip=}"
;;
ip6=*)
IP6="${x#ip6=}"
;;
boot=*)
BOOT=${x#boot=}
;;
ubi.mtd=*)
UBIMTD=${x#ubi.mtd=}
;;
resume=*)
RESUME="${x#resume=}"
case $RESUME in
UUID=*)
RESUME="/dev/disk/by-uuid/${RESUME#UUID=}"
esac
;;
resume_offset=*)
resume_offset="${x#resume_offset=}"
;;
noresume)
noresume=y
;;
drop_capabilities=*)
drop_caps="-d ${x#drop_capabilities=}"
;;
panic=*)
panic="${x#panic=}"
case ${panic} in
*[![:digit:].]*)
panic=
;;
esac
;;
ro)
readonly=y
;;
rw)
readonly=n
;;
debug)
debug=y
quiet=n
if [ -n "${netconsole}" ]; then
exec >/dev/kmsg 2>&1
else
exec >/run/initramfs/initramfs.debug 2>&1
fi
set -x
;;
debug=*)
debug=y
quiet=n
set -x
;;
break=*)
break=${x#break=}
;;
break)
break=premount
;;
blacklist=*)
blacklist=${x#blacklist=}
;;
netconsole=*)
netconsole=${x#netconsole=}
[ "x$debug" = "xy" ] && exec >/dev/kmsg 2>&1
;;
BOOTIF=*)
BOOTIF=${x#BOOTIF=}
;;
hwaddr=*)
BOOTIF=${x#hwaddr=}
;;
fastboot|fsck.mode=skip)
fastboot=y
;;
forcefsck|fsck.mode=force)
forcefsck=y
;;
fsckfix|fsck.repair=yes)
fsckfix=y
;;
fsck.repair=no)
fsckfix=n
;;
recovery)
recovery=y
;;
esac
done

# Default to BOOT=local if no boot script defined.
if [ -z "${BOOT}" ]; then
BOOT=local
fi

if [ -n "${noresume}" ] || [ "$RESUME" = none ]; then
export noresume=y
unset resume
else
resume=${RESUME:-}
fi

maybe_break top

# export BOOT variable value for compcache,
# so we know if we run from casper
export BOOT

# Don't do log messages here to avoid confusing graphical boots
run_scripts /scripts/init-top

maybe_break modules
[ "$quiet" != "y" ] && log_begin_msg "Loading essential drivers"
[ -n "${netconsole}" ] && modprobe netconsole netconsole="${netconsole}"
load_modules
[ "$quiet" != "y" ] && log_end_msg

maybe_break premount
[ "$quiet" != "y" ] && log_begin_msg "Running /scripts/init-premount"
run_scripts /scripts/init-premount
[ "$quiet" != "y" ] && log_end_msg

maybe_break mount
log_begin_msg "Mounting root file system"
# Always load local and nfs (since these might be needed for /etc or
# /usr, irrespective of the boot script used to mount the rootfs).
. /scripts/local
. /scripts/nfs
. /scripts/${BOOT}
parse_numeric ${ROOT}
maybe_break mountroot
mount_top
mount_premount
mountroot
log_end_msg

if read_fstab_entry /usr; then
log_begin_msg "Mounting /usr file system"
mountfs /usr
log_end_msg
fi

# Mount cleanup
mount_bottom
nfs_bottom
local_bottom

maybe_break bottom
[ "$quiet" != "y" ] && log_begin_msg "Running /scripts/init-bottom"
# We expect udev's init-bottom script to move /dev to ${rootmnt}/dev
run_scripts /scripts/init-bottom
[ "$quiet" != "y" ] && log_end_msg

# Move /run to the root
mount -n -o move /run ${rootmnt}/run

validate_init() {
run-init -n "${rootmnt}" "${1}"
}

# Check init is really there
if ! validate_init "$init"; then
echo "Target filesystem doesn't have requested ${init}."
init=
for inittest in /sbin/init /etc/init /bin/init /bin/sh; do
if validate_init "${inittest}"; then
init="$inittest"
break
fi
done
fi

# No init on rootmount
if ! validate_init "${init}" ; then
panic "No init found. Try passing init= bootarg."
fi

maybe_break init

# don't leak too much of env - some init(8) don't clear it
# (keep init, rootmnt, drop_caps)
unset debug
unset MODPROBE_OPTIONS
unset DPKG_ARCH
unset ROOTFLAGS
unset ROOTFSTYPE
unset ROOTDELAY
unset ROOT
unset IP
unset IP6
unset BOOT
unset BOOTIF
unset UBIMTD
unset blacklist
unset break
unset noresume
unset panic
unset quiet
unset readonly
unset resume
unset resume_offset
unset fastboot
unset forcefsck
unset fsckfix

# Move virtual filesystems over to the real filesystem
mount -n -o move /sys ${rootmnt}/sys
mount -n -o move /proc ${rootmnt}/proc

# Chain to real filesystem
exec run-init ${drop_caps} ${rootmnt} ${init} "$@" ${recovery:+--startup-event=recovery} <${rootmnt}/dev/console >${rootmnt}/dev/console 2>&1
echo "Something went badly wrong in the initramfs."
panic "Please file a bug on initramfs-tools."

またこのinitを実行するタイミングではルートファイルをマウントしていないため、ルートにある/binや/sbinを利用できません。

代わりに、initrd内部の/binや/sbinを利用しています。

そのため、ここにバイナリを配置しておく事で起動中に使用できる様になります。

注意
バイナリの実行のために、別途moduleの追加が必要だったりもします。

initrdを作成する

展開後のファイルが配置されているフォルダで行います。
cpioによるアーカイブ化、gzipによる圧縮を同時に行い元の状態に戻します。

1
$ find | cpio -o -H newc  | gzip -c > ../initrd.img-4.15.0-54-generic

作成されたinitrdを元の/boot/initrdと入れ替えても起動する事が確認できるかと思います。

initrdの編集は以上の操作で行います。

init実行中に処理を止める

/boot/grub/grub.cfgのlinuxオプションが書かれている箇所にbreak=initとオプションを追加する事で処理が停止します。
initrd内のデバッグは、このタイミングで一旦止め、initrd内のリソースを使用したコマンド操作が可能です。
処理を進める場合には、exitコマンドを実行し、続行させる事ができます。

注意
HyperV上に起動したLinuxMint19では、このタイミングではコンソールの描画が行われませんでした。
そのため実機で行う事をおすすめします。

前提環境

  • Ubuntu

GitHubに公開鍵を追加する理由

  • sshを使ってリモートリポジトリの操作を出来る様になる
  • GitHub上にあるリポジトリをsshで操作する様に作られた手順、ツールが沢山ある

注意
sshを使わなくてもhttpで操作する事が可能
ただし、その場合はGitHubアカウントのパスワード入力が多々必要になる

手順

秘密鍵・公開鍵を作成する

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$ ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/root/.ssh/id_rsa): sakkuntyo-mint #鍵の名前です、今回はLinuxMint用に作成したためこの名前
Enter passphrase (empty for no passphrase): #パスワード、無くても良い
Enter same passphrase again: #パスワードの再確認
Your identification has been saved in sakkuntyo-mint.
Your public key has been saved in sakkuntyo-mint.pub.
The key fingerprint is:
SHA256:7XG5j/GRtAxIs3/dkUxVIZDrcyG+6g7LVxqvNVK1bEg root@DESKTOP-2QSQU2N
The key's randomart image is:
+---[RSA 2048]----+
| .o. .=|
| . . .|
| oE.. . |
| o.*+o+ .|
| S Bo=+o+ |
| o.O.* +o|
| . .*oB * o|
| . oooo.* . |
| o+=o . o |
+----[SHA256]-----+

鍵ファイルが出来た事を確認する

1
2
$ ls
sakkuntyo-mint sakkuntyo-mint.pub

.pubが付いているのが公開鍵
ついていないのが秘密鍵

GitHubに配置する

github.comのsettingsを開く

github-settings
github-settings2

「SHS and GPG keys」を開き、公開鍵の名前と公開鍵の内容を登録

Titleは何でも大丈夫

github-settings
github-settings

これで公開鍵が登録されました

sshクライアントにgitの設定を追加

~/.ssh/configを編集し、以下の4行を追加

1
2
3
4
Host git
hostname github.com
user git
identityfile ~/.ssh/sakkuntyo-mint #秘密鍵のパス

秘密鍵をconfigで指定した場所に置きなおす

1
$ mv sakkuntyo-mint ~/.ssh/

秘密鍵の権限を自分だけが読める様に変更

1
$ chmod 0600 ~/.ssh/sakkuntyo-mint

プライベートリポジトリをcloneしてみる

プライベートリポジトリをhttpでcloneしようとするとパスワードが求められますが
sshを使ってcloneした場合にはパスワードは求められなくなります

1
$ git clone git:sakkuntyo/private-repo

必要な基礎知識

  • リポジトリ
    • リモートリポジトリ
      • 皆で操作するために作られる
    • ローカルリポジトリ
      • 自分が操作するために作られる
  • ブランチ
    • リポジトリに含まれる機能
    • デフォルトでmasterブランチが作られる
    • ブランチ同士を突き合わせて差分を見てからマージを行う
  • ワーキングツリー
    • 現在のファイルの状態
  • インデックス
    • 現在のファイルの管理状態
    • git add “ファイル名” と操作を行うと、指定したファイルがステージング状態(コミットに含まれる状態)となる
    • インデックスをリセットしたい場合には、git resetする
    • 状態
      • ステージング
        • 次のコミットに含まれる状態
      • 非ステージング
        • 次のコミットに含まれない状態
      • 非トラッキング
        • ブランチ内で全く管理していない状態
  • コミット
    • ファイルの状態を確定する操作
    • ログを見たり、以前の状態に戻す際に必要となる
  • プッシュ
    • ローカルで変更されたブランチをリモートリポジトリに反映させる際に行う操作

リポジトリ作成からプッシュまでの手順

基本的にはコマンドを使うことは無いかと思いますが、ここではコマンド操作による説明を行います。

1.リモートリポジトリを作成する

1
2
3
4
> mkdir "プロジェクト名"
> cd "プロジェクト名"
> git init --bare
Initialized empty Git repository in /path/プロジェクト名/

2.リモートリポジトリを複製し、ローカルリポジトリを作成する

1
2
3
> mkdir work
> cd work
> git clone "1で作成したフォルダのパス"

3.ブランチを分ける

1
> git checkout -b "ブランチ名"

ポイント
checkout -bでは、ブランチの作成と切替を同時に行います。

4.現在のブランチを確認する

1
> git branch -a -vvv

ポイント1
*がついているブランチが現在選択中のブランチとなります。

ポイント2
remotesで始まる名前の物は、リモートリポジトリにあるブランチとなります。

注意
masterブランチに1つもコミットが無い状態での
ブランチ作成、切替を行った状態では、何も表示されない様です。

5.変更

ファイルを修正、追加するなどして変更を加えます

6.git add前に状態を確認

Untracked files(非トラッキング)となっている事がわかります。

1
> git status

7.git addする

1
> git add ""今回新規作成されたファイル"

8.ステージング後に状態を確認

Changes to be commited(ステージング)に含まれている事がわかります。

1
> git status

9.変更を確定する

1
> git commit -m "ログに残すメッセージ"

10.ログを確認する

1
> git log

11.プッシュする

1
> git push origin HEAD

ポイント
originとは、clone時に自動で作成されるリモートリポジトリのURLの名前となります。
HEADとは、現在のブランチの最新コミットの状態を指します。
この二つを組み合わせる事で、ブランチ名を指定する事なく、プッシュする事が可能です。