師走が近くなって参りましたが、皆さんはいかがお過ごしでしょうか。
冬の到来も近いので、OSを変更します。 移行先はAlpine Linux、musl libcで BusyBoxな小さめのナウでヤンクな人達に大人気のディストリビューションです。 古事記にもそう記されている。
まともなディストリビューションなので、基本的な事(X11の入れ方やら)はWikiに書いてある事もあり、書いていない事もある。 という訳で、忘れちゃならないティップス集です。
聖典はこれなんですが、 Arch Linuxに比べると簡単ですね、まるでDebianみたい。
他のディストリでよく採用されているDHCPクライアント、dhcpcdはwpa_supplicantとよしなに連携してくれますが、BusyBox同梱のudhcpcはそんな事をしてくれません。
アクセスポイントが変わる度にudhcpc -iwlan0
やらそんな感じのことを叩きたくないので、どうにかシュッとさせる必要がある。
という訳で、wpa_supplicantに同梱されたwpa_cliのフック機能を使って手動でudhcpcをよしなにします。 例えばこんな感じで。
/etc/network/interfaces
auto lo
iface lo inet loopback
auto wlan0
iface wlan0 inet manual
# wpa_supplicant calls udhcpc
/etc/wpa_supplicant/wpa_cli.sh
#!/bin/sh
UDHCPC='/sbin/udhcpc'
if [[ -x "${UDHCPC}" ]]; then
logger -t wpa_cli "${UDHCPC} doesn't exist"
exit 1
elif [[ -z "${1}" -o -z "${2}" ]]; then
logger -t wpa_cli "Usage: ${0} IFNAME ACTION"
exit 1
fi
IFNAME="${1}"
ACTION="${2}"
PIDFILE="/var/run/udhcpc.${IFNAME}.pid"
case "${ACTION}" in
CONNECTED)
if [[ -f "${PIDFILE}" ]]; then
kill -s USR1 `cat "${PIDFILE}"`
else
${UDHCPC} -R -n -p "${PIDFILE}" -i${IFNAME}
fi
;;
DISCONNECTED)
[[ -f "${PIDFILE}" ]] && \
${KILL} `cat "${PIDFILE}"`
;;
*)
logger -t wpa_cli "unknown action '${ACTION}'"
exit 1
esac
/etc/init.d/wpa_cli
#!/sbin/openrc-run
description='wpa_cli action daemon'
name='wpa_cli'
command='/sbin/wpa_cli'
pidfile='/var/run/wpa_cli.pid'
command_args="-P ${pidfile} -B -a ${wpa_cli_action:-/etc/wpa_supplicant/wpa_cli.sh}"
depend () {
need wpa_supplicant
after wpa_supplicant
}
あとはrc-update add wpa_cli
すれば、次回起動時から、wpa_cliが
udhcpcをdhcpcd並みによしなにシュッしてくれるようになります。
OpenRCはsystemd並みにinitファイルが書きやすくてキモティーですね。
日本語が打てない。何故でしょうか。
$ apk search ibus
live-media-2016.04.21-r0
libusb-1.0.20-r0
usbredir-0.7-r2
libusb-compat-0.1.5-r3
libusb-dev-1.0.20-r0
usbredir-dev-0.7-r2
libusb-compat-dev-0.1.5-r3
$ apk search fcitx
$ apk search uim
$ apk search scim
はい。という訳で、IMEを自分でコンパイルして入れましょう。 今回は、skkが組み込まれているuimを採用しました。
# apk add \
libtool gtk+-dev libx11-dev gcc make \
g++ gtk+2.0-dev libxext-dev gettext \
fortify-headers freetype-dev \
libintl
$ wget https://storage.googleapis.com/google-code-archive-downloads/v2/code.google.com/uim/uim-1.8.6.tar.gz
$ ./configure \
--disable-nls --disable-gnome-applet \
--enable-gnome3-applet=no --enable-qt4-qt3support=no \
--disable-fep --disable-emacs --enable-kde-applet=no \
--enable-kde4-applet=no --without-m17nlib --without-anthy \
--with-mana=no --with-prime=no --with-gtk3=no --with-x
$ make
# make install
# apk del -r --purge \
automake autoconf libtool gtk+-dev libx11-dev gcc \
g++ gtk+2.0-dev libxext-dev gettext \
fortify-headers freetype-dev
configureやらは趣味でなんたらやって下さい。 Alpine Linuxのレポジトリにあるfirefoxは、XIMサポートが切られているようなので、 gtkのサポートを入れておくと時間を無駄にせずにすみます。 これは豆知識なのですが、gettextが無くてもuimは動く。
あと、下のような感じでuimを自動起動させると良いです。
$ cat << _EOF_ >> ~/.xinitrc
export XMODIFIERS='@im=uim'
export GTK_IM_MODULE='uim'
export GTK_IM_MODULE_FILE='/usr/local/etc/gtk-2.0/gtk.immodules'
uim-xim &
_EOF_
powertopがレポジトリにありますが、debugfsにアクセスできないとか言って起動できません。 頑張りが重要です。こんな感じで。
/etc/local.d/00-powersave.start
#!/bin/sh
# Enable Laptop-Mode disk writing
echo 5 > /proc/sys/vm/laptop_mode
# NMI watchdog should be turned on
for i in /proc/sys/kernel/nmi_watchdog
do
echo 0 > $i
done
# Set SATA channel to power saving
for i in /sys/class/scsi_host/host*/link_power_management_policy
do
echo min_power > $i
done
# Select Ondemand CPU Governor
for i in /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor
do
echo ondemand > $i
done
# Activate USB autosuspend
for i in /sys/bus/usb/devices/*/power/level
do
echo auto > $i
done
# Activate PCI autosuspend
for i in /sys/bus/pci/devices/*/power/control
do
echo auto > $i
done
# Activate audio card power saving
# (sounds shorter than 5 seconds will not be played)
echo 5 > /sys/module/snd_hda_intel/parameters/power_save
echo 1 > /sys/module/snd_hda_intel/parameters/power_save_controller
どこかから教授した謎のスクリプト(modifired)なのですが、どこから引っ張ってきたか忘れた。
# addgroup MYUSER lp
# cat << _EOF_ >> /etc/modules-load.d/uhid.conf
uhid
_EOF_
にゃん。
flashpluginはバイナリでしか配布されていません。
そのせいで、glibcの入っていないAlpine Linuxでは動きません。
フッラシュのためだけに仮想マシンブッチ上げしたり、
glibcをブチ込むのは悲しすぎるので、
firefox on Debian on Docker
で素早くコンテナーしましょう。
# apk add docker
# addgroup USER docker
で終わり……と行きたい所ですが、Dockerがchrootして色々しているせいで、Grsecurityが怒ります。
# cat << _EOF_ >> /etc/sysctl.conf
kernel.grsecurity.chroot_deny_chmod = 0
kernel.grsecurity.chroot_deny_mknod = 0
_EOF_
# rc-update add sysctl
# rc-service start sysctl
こうすれば、さすがのGrsecurityクンもニッコリ。
なお、Docker v1.12.0からchrootの代わりにpivot_rootを使うようになったので、 このようなワーカアランウドは必要無くなりました。しかし2016年ポッキーデイ現在、 レポジトリ上にあるDockerはv1.11.2です。
X11はネットワーク透過性があり、この点については非常に簡単に解決できます。
/tmp/.X11-unix/
とDISPLAY
環境変数をコンテナに露出させるだけです。
こんな感じですね。
$ docker run \
--volume=/tmp/.X11-unix:/tmp/.X11-unix \
--env=DISPLAY
[IMAGE] [ENTRYPOINT]
ただソケットをexposeするだけだと、X11の認証機構を通過できないので、事前にxhost local:
やら打って認証機構を殺しましょう。
${HOME}/.Xauthority
を上手にexposeしたり、hostnameをちゃんと設定したり、
xauthをよしなに弄ったりすれば、認証機構を殺さずによしなにできるっぽいですが、面倒なのでやりません。
ALSAにネットワーク透過性がありません。
aserver
なる、謎のコマンドラインツールがあるっぽいのですが、使い方が分かりません。
なんといってもまず、ドキュメントが無い。
こんな時、一般的にはPulseAudioを使うらしいのですが、 素晴らしい事に、レポジトリにそんな物はありません。 そこでJACKを使う。これで解決、皆満足。 い まずはJACKを入れます。
# apk add jack jack-example-clients
次に、全ての音がJACKを経由するようによしなに設定します。 ココには全てが書いてあります。下にはその一部が具体的に記されています。
/etc/modules-load.d/alsa.conf
snd-aloop
ALSAでLoopbackデバイスを使えるようにして……
${HOME}/.asoundrc
pcm.amix {
type dmix
ipc_key 1024
slave {
pcm "hw:Loopback,0,0"
format S32_LE
}
}
pcm.to_jack {
type plug
slave {
pcm "hw:Loopback,1,0"
format S32_LE
}
}
pcm.!default {
type plug
slave {
pcm "amix"
format S32_LE
}
}
ctl.!default {
type hw
card 1
}
全ての音がpcm.to_jack
に飛ぶようにして……
$ jackd -r -d alsa -P hw:1 &
JACKを起動して……
$ alsa_in -j alsa_in -d to_jack -r 48000 &
pcm.to_jackから音声を拾ってくるalsa_in
を起動して……
$ jack_connect alsa_in:capture_1 system:playback_1
$ jack_connect alsa_in:capture_2 system:playback_2
alsa_inから流れてくる音声をsystem:playback
に流すようにして、はい。
お次はドッカーコンテナーを起動します。
container_name=container
init_jacknet () {
local container_ip=`docker inspect --format='{{ .NetworkSettings.IPAddress }}' $1`
local jack_cli="netjack-${key}"
local container_ip=`get_container_ip ${container_id}`
jack_netsource -N ${jack_cli} -H ${container_ip} -I0 -O0 -i2 -o0 &
sleep 0.1 # wait for jack_netsource initialized
jack_connect ${jack_cli}:capture_1 system:playback_1
jack_connect ${jack_cli}:capture_2 system:playback_2
}
(
sleep 1 # wait for docker container initialized
init_jacknet ${container_name}
) &
# 1秒以内に素早く起動しましょう
docker run \
--name=${container_name}
[IMAGE] [ENTRYPOINT]
jack_netsource -H [IP]
で、[IP]からの音を取ってくる事ができるようになります。
jack_connect
でsystem:playbackに繋げておけば、スピーカーから音が聞こえてくるようになり安心。
ここで、ホスト側の作業は終わり、ここからコンテナ内での作業が始まります。
${HOME}/.asoundrc
pcm.jack {
type jack
playback_ports {
0 system:playback_1
1 system:playback_2
}
}
pcm.!default {
type plug
slave.pcm jack
}
ホストの時と同じく、ALSAを流れる全ての音がJACKを経由するように、.asoundrc
を設定します。
今回はJACKのALSAプラグインを使ってみました。
件のプラグインは、Debianなら、apt install libasound2-plugins
で入るゾイ。
jackd -r -d netone &
後は、JACKを起動すれば完了です。
コンテナ側でjack_simple_client
して謎の音が流れてくれば、成功だぞ。
アレをこうすれば終わりなのですが、何をやっているかは上を読んで察してくれ。
${HOME}/flashfox/Dockerfile
FROM debian:jessie
ENV DEBIAN_FRONTEND noninteractive
RUN echo deb http://deb.debian.org/debian jessie contrib >> /etc/apt/sources.list && \
apt-get update && \
apt-get install --no-install-recommends -y \
flashplugin-nonfree firefox-esr fonts-ipaexfont \
jackd libasound2-plugins dbus-x11 paxctl && \
rm -rf /var/lib/apt/lists/* && \
# disabling MPROTECT is required for flashplugin/firefox
paxctl -cm /usr/lib/firefox-esr/firefox-bin && \
paxctl -cm /usr/lib/firefox-esr/plugin-container && \
apt-get purge -y paxctl && \
useradd -m firefox
COPY ["start-fx.sh", "/"]
USER firefox
ENTRYPOINT ["/start-fx.sh"]
MPROTECTを無効にしないと、firefoxがGrsecurityに殺されるぞ。
${HOME}/flashfox/start-fx.sh
#!/bin/sh
set -ue
umask 0027
export PATH='/bin:/usr/bin'
export LANG='C'
IFS='
'
# copy profile
printf 'Initializing profile...\n'
mkdir "${HOME}/.mozilla"
cp -r /profile/* "${HOME}/.mozilla"
# create asoundrc
cat << _EOF_ > "${HOME}/.asoundrc"
pcm.jack {
type jack
playback_ports {
0 system:playback_1
1 system:playback_2
}
}
pcm.!default {
type plug
slave.pcm jack
}
_EOF_
# initialize jack
/usr/bin/jackd -r -d netone &
# start firefox!
LANG=ja_JP.UTF-8 /usr/bin/firefox --no-remote --new-instance [email protected]
プロファイルレポジトリに書き込めないと死ぬのでどうにかしています。
${HOME}/.asoundrc
pcm.amix {
type dmix
ipc_key 1024
slave {
pcm "hw:Loopback,0,0"
format S32_LE
}
}
pcm.to_jack {
type plug
slave {
pcm "hw:Loopback,1,0"
format S32_LE
}
}
pcm.!default {
type plug
slave {
pcm "amix"
format S32_LE
}
}
ctl.!default {
type hw
card 1
}
${HOME}/docker-run-gui.sh
#!/bin/sh
set -ue
umask 0027
export PATH='/bin:/usr/bin'
export LANG='C'
IFS='
'
CONTAINER_KEY_DICTKEY='domestic.graphic.container_key'
die () {
local status=${1}
printf 'Usage: %s [-d $HOME dir] [-o opts] [-ph] tag entrypoint\n' "$0"
exit $status
}
get_container_id () {
printf '%s' `docker ps -ql --filter="label=${CONTAINER_KEY_DICTKEY}=${1}"`
}
get_container_ip () {
printf '%s' `docker inspect --format='{{ .NetworkSettings.IPAddress }}' ${1}`
}
initialize_jack () {
local key=${1}
local info_fifo=${2}
local container_id=`get_container_id ${key}`
test -z "${container_id}" && return
local jack_cli="netjack-${key}"
local container_ip=`get_container_ip ${container_id}`
jack_netsource -N ${jack_cli} -H ${container_ip} -I0 -O0 -i2 -o0 &
sleep 0.1 # wait for jack_netsource initialized
jack_connect ${jack_cli}:capture_1 system:playback_1
jack_connect ${jack_cli}:capture_2 system:playback_2
echo $! >> "jack_pid:${pid_fifo}"
}
finalize_jack () {
local fifo_netsrc_pid="${1}"
local netsrc_pid=`cat "${fifo_netsrc_pid}"`
test -n "${netsrc_pid}" && kill "${netsrc_pid}"
rm -f "${fifo_netsrc_pid}"
}
while getopts npho:d: opt; do
case "${opt}" in
o) opts="${opts:-} ${OPTARG}" ;;
d) home="${OPTARG}" ;;
p) privileged='true' ;;
n) no_jack='true' ;;
h) die 0 ;;
[?]) die 1 ;;
esac
done
shift `expr ${OPTIND} - 1`
test -z "${1:-}" && die 1
tag=${1:-}
shift 1
entrypoint="${@:-}"
container_key=`cat /dev/urandom | tr -dc A-Za-z0-9 | head -c 16`
if test -z "${no_jack:-}"; then
fifo_netsrc_pid=`mktemp -u`
(
sleep 0.1 # wait for docker container initialized
mkfifo -m 600 "${fifo_netsrc_pid}"
initialize_jack ${container_key} "${fifo_netsrc_pid}"
) &
trap "finalize_jack '${fifo_netsrc_pid}'" EXIT
fi
test -z "${privileged:-}" && \
opts="${opts:-} --user='`id -u`:`id -g`'"
xhost local: # bypass MIT-COOKIE authentication
docker run --rm \
--volume='/tmp/.X11-unix:/tmp/.X11-unix' \
--env="DISPLAY" \
--env="HOME=${home:-/tmp}" \
--label="${CONTAINER_KEY_DICTKEY}=${container_key}" \
${opts:-} ${tag} ${entrypoint}
なんか、異常に壮大なシェルスクリプトがありますが、やってる事は似たような事です。 後は、下のように実行すればイナフです。
$ jackd -r -d alsa -P hw:1 > /dev/null &
$ # wait for jack initialized
$ alsa_in -j alsa_in -d to_jack -r 48000 > /dev/null &
$ # wait for alsa_in initialized
$ jack_connect alsa_in:capture_1 system:playback_1
$ jack_connect alsa_in:capture_2 system:playback_2
$ cd ${HOME}/flashfox
$ docker build -t flashfox .
$ ./docker-run-gui.sh -d'/tmp' \
-o"--volume=${HOME}/.mozilla:/profile:ro" \
flashfox --private-window
遠回りした感じはありますが、やっと目的地に到着しました。
見て分かる通り、非常に面倒なので、.xinitrc
をイジるなり、
シェルスクリプトにしてゴチョゴチョするなりなんなりしておくといいんじゃないでしょうか。