KeePass を使う

パスワード管理はずっと Firefox のを使ってたんだけど、9月頃から KeePassXC を使い始めた。しばらく使ってみてそれなりに使えてるのでメモ。なお KeePass と KeePassXC の違いはよくわかってない。

KeePassXC はデータベースを開くためにパスワードだけじゃなくてキーファイルも必要とできるので、データベースファイルはクラウド上に置いて、キーファイルを Ubuntu やスマホのローカルに置くという構成にしてる。

Ubuntu 上で Firefox の abount:logins から「ログイン情報をエクスポート」で CSV で出力しておく。

KeePassXC のインストールと設定

Ubuntu で keepassxc をインストール:

% apt install keepassxc

アプリから KeePassXC を起動:

「CSVからインポートする」でエクスポートした CSV を選択。

データベース名を入力:

「データベースの資格情報」でパスワードを指定し、「保護を追加…」でキーファイルを追加する。キーファイルの追加はあとでもできる。

CSV フィールドの列の関連付けはこんな風にしたら良さそうな感じ?

ここで一旦 KeePassXC を終わらせる。ウィンドウを閉じただけだと終わってないので、メニューから「終了」を選ぶ。

Googleドライブ上にデータベースファイルを置く

Android でも使いたいので、データベースファイルを Google ドライブ上に置く。 Ubuntu で Google ドライブをマウントする方法はいくつかあるみたいだけど、rclone と systemd を使うのが一番良さそうだった。 rcloneでGoogleDriveとかDropBoxとかをお洒落にマウントする - おしゃれな気分でプログラミング を参考にした。というかそのまんま。

rclone をインストール:

% sudo apt install rclone

~/.config/systemd/user/home-tommy-gdrive.mount というファイルを次の内容で作成:

[Unit]
After=network-online.target
[Mount]
Type=rclone
What=drive:
Where=/home/tommy/gdrive
Options=vfs-cache-mode=full
[Install]
WantedBy=default.target

systemctl コマンドでマウントできる:

% systemctl --user start /home/tommy/gdrive

ちゃんとマウントできてる:

% mount | grep gdrive
drive: on /home/tommy/gdrive type fuse.rclone (rw,nosuid,nodev,relatime,user_id=1000,group_id=1000)

マウントを解除するには systemctl で

% systemctl --user stop home-tommy-gdrive.mount

とやってもできるけど、umount の方が簡単:

% umount gdrive

自動的にマウントされるようにするには systemctl で:

% systemctl --user enable home-tommy-gdrive.mount

KeePassXC で作成したデータベースファイルをマウントしたディレクトリ配下にコピーして、次に KeePassXC を起動したときにそのファイルを指定すればOK。

スマホに Keepass2Android をインストール

Android 用の KeePass アプリはいくつかあるけど、Keepass2Android を使ってみた。

あらかじめ、上記で作ったキーファイルをどうにかして Android 上にコピーする。自分は家庭内NASがあるのでそれ経由でコピーした。Ubuntu と Android を USB ケーブルで接続してもコピーできると思う。 Google ドライブ使ってコピーしてもいいけど、データベースファイルとキーファイルが同じ場所に置いてあるとキーファイルを使ってる意味がないので、コピー後は Google ドライブ上から消しておくこと。

「ファイルを開く」から「Googleドライブ」を選択して、上で Google ドライブに置いたデータベースファイルを選択。

「マスターキーの種類を選択」で「パスワード+キーファイル」を選択し、パスワードとキーファイルを指定する。

Android の設定の「パスワードを管理」→「自動入力サービス」で Keepass2Android を選択する。

アプリでログインするときに Keypass2Android を使って自動入力できるようになる。

おわり

これでアカウントを Ubuntu から登録しても Android から登録しても両方から使えるようになった。ときどき Ubuntu から登録したアカウントが Android で見れないことがあるけっど、よくわかってない。データベースを開き直したら使える。

まだいまいち使い方がわかってないところはあるけど、なんとなく使えてるからまあいいかな〜。

Google ドライブが使えなくなるとパスワードが失われてしまうのはアレなので、定期的にローカルにバックアップするようにしてる。

年が明ける前に書いた。えらい!

ThinkPad T14 Gen3 + Xubuntu 22.10 をハイバネートできるようにしてみた

ハイバネート

Ubuntu 22.04でのハイバネーション - Journal InTime(2023-01-03) を見て、自分の ThinkPad T14 Gen3 はそんなでもないけどサスペンド時にまあまあバッテリーが減るので、自分もハイバネートしようかと思ってやってみた。

ハイバネートの設定手順は上のブログに書いてあるのそのまま:

# swapoff -a300
# grep -q swapfile/etc/fstab ||  echo "/swapfile  none  swap  sw  0  0" >> /etc/fstab
# fallocate -l 32G /swapfile
# chown 0 /swapfile
# chmod 0600 /swapfile
# mkswap /swapfile
# swapon /swapfile
# findmnt / -o UUID
UUID
90263320-8701-4639-b7b9-9705677d9c7a
# filefrag -v /swapfile |grep " 0:"| awk '{print $4}'
34816..

/etc/default/grub を編集:

GRUB_CMDLINE_LINUX_DEFAULT="quiet splash resume=UUID=90263320-8701-4639-b7b9-9705677d9c7a resume_offset=34816"
# update-grub

メモリ 24GB なのでスワップは 32GB にした。 ファイルシステム上にあるスワップファイルの物理位置を指定するのってなんか怖いんだけど大丈夫なのかな。 スワップ専用パーテイションを用意する方が安全なんじゃないかと思ってしまう。

ハイバネートのテスト:

# systemctl hibernate

ちゃんとハイバネートして、電源ボタンで復帰すればOK。

でも復帰時に画面が真っ暗のままで syslog に延々と [drm] *ERROR* Fault errors on pipe A: 0x00000080 が出力されてしまう。

その状態で Ctrl-Alt-F1 押して Alt-F7 を押すとログイン画面が表示されてログも止まるんだけど、ログがすごい勢いで溜まってディスクを圧迫するので止めたい。

どうやらカーネル 6.0.3 で直ってるらしいので、mainline から 6.0.19 を取ってきてインストールしたら直った。

6.1 を使ったほうがいいのかな、実は…。

蓋を閉じたときにハイバネート

Xubuntu は蓋を閉じたときの振る舞いとしてハイバネートを設定できない。 何かやればできるようになるのかもしれないけど、軽くググったくらいだと見つからなかったので自力でなんとかする。

LID イベントを拾えればいいので、入力デバイスを調べる:

% sudo evtest
No device specified, trying to scan all of /dev/input/event*
Available devices:
/dev/input/event0:  Sleep Button
/dev/input/event1:  Lid Switch
/dev/input/event10: sof-hda-dsp Mic
/dev/input/event11: sof-hda-dsp Headphone
/dev/input/event12: sof-hda-dsp HDMI/DP,pcm=3
/dev/input/event13: sof-hda-dsp HDMI/DP,pcm=4
/dev/input/event14: sof-hda-dsp HDMI/DP,pcm=5
/dev/input/event15: Integrated Camera: Integrated C
/dev/input/event16: rkremap
/dev/input/event2:  Power Button
/dev/input/event3:  AT Translated Set 2 keyboard
/dev/input/event4:  ThinkPad Extra Buttons
/dev/input/event5:  ELAN0676:00 04F3:3195 Mouse
/dev/input/event6:  TPPS/2 Elan TrackPoint
/dev/input/event7:  Video Bus
/dev/input/event8:  Intel HID events
/dev/input/event9:  ELAN0676:00 04F3:3195 Touchpad
Select the device event number [0-16]: ^C

/dev/input/event1 がそれっぽい。 自作 gem の rkremap をインストールした状態で、こんな感じにすると蓋を閉じたイベントを拾って systemctl hibernate を実行することができた。

% sudo ruby -rrkremap -e 'dev = Rkremap::Evdev.new("/dev/input/event1"); dev.grab; loop{e = dev.read_event; system("systemctl hibernate") if e.ev_type == 5 && e.value == 1 }'

grab してるので普通の電源設定の方のサスペンドとかが動いちゃうこともない。 もっと簡単な方法もあるような気がするけどまあいいや。

サスペンドしてしばらくしてからハイバネート

ハイバネートはサスペンドに比べると復帰までに時間が掛かるので、蓋を閉じるたびにハイバネートするのはいまいち。 サスペンドしてしばらくしてからハイバネートする suspend-then-hibernate というのがあったので、これを使ってみる。

% sudo ruby -rrkremap -e 'dev = Rkremap::Evdev.new("/dev/input/event1"); dev.grab; loop{e = dev.read_event; system("systemctl suspend-then-hibernate") if e.ev_type == 5 && e.value == 1 }'

サスペンドしてからハイバネートするまでの時間は /etc/systemd/sleep.conf の HibernateDelaySec で設定できる。デフォルトは 120min らしい。ちょっと長いので 30min にしてみた。

勝手にハイバネートが解除される

これで完成!…と思ったけど、蓋閉じてるのになぜか普通に電源が入ってバッテリを食い尽くしてサスペンドになっていたことがあった。

systemctl hibernate を何回か実行して試してみたら 4〜5回に1回くらいはハイバネート直後に復帰しちゃう。

復帰トリガー - ArchWiki を見て、次のように対処してみた。

S4(ハイバネート)の復帰トリガーとして有効なものをリスト:

% cat /proc/acpi/wakeup | grep 'S4.*enabled'
PEG0      S4    *enabled   pci:0000:00:06.0
TXHC      S4    *enabled   pci:0000:00:0d.0
TDM0      S4    *enabled   pci:0000:00:0d.2
TDM1      S4    *enabled   pci:0000:00:0d.3
TRP0      S4    *enabled   pci:0000:00:07.0
TRP2      S4    *enabled   pci:0000:00:07.2
AWAC      S4    *enabled   platform:ACPI000E:00
LID       S4    *enabled   platform:PNP0C0D:00

LID は蓋なので有効でもいい。それ以外はよくわからなかったけど echo XXX > /proc/acpi/wakeup で全部無効化した。

この状態で試してみたら勝手に復帰しちゃうことはなくなったっぽいので、この設定を永続化する。 udev をいじったりすると永続化設定できるみたいなんだけど、難しくてよくわからなかったので、起動スクリプトを作った。

/etc/init.d/disable-wakeup:

#!/bin/sh
### BEGIN INIT INFO
# Default-Start: 2 3 4 5
### END INIT INFO
cat /proc/acpi/wakeup | grep 'S4.*enabled' | awk '{print $1}' | grep -v LID | while read x; do
  echo "$x" > /proc/acpi/wakeup
done

というファイルを作って、 sudo update-rc.d disable-wakeup defaults で Linux 起動時に自動実行されるようにした。

しばらく使ってみてるけど大丈夫っぽい。 念の為、電源設定で、バッテリ駆動時に無操作状態が15分続くとサスペンドするように設定しておいてある。

ThinkPad トラックポイントキーボード II を買った

2013年10月に ThinkPad トラックポイントキーボード(旧型)の日本語配列を買って以来ずっと使ってるんでもう9年弱。その前は Happy Hacking Keyboard だった。

転職して会社の PC が Mac になったので、できるだけキー配列を Mac に合わせて [無変換] [変換] キーを [command] キーに割り当てた。Linux だと [Super] キーに割り当ててて、Mac でも Linux でも [無変換]+[Tab] でアプリを切り替えられるようにしてみた。

ところが [無変換]+[Tab]+[左Shift] の同時押しを認識してくれなくて、アプリ切替時に逆方向に進めることができない。Mac でも Linux でも。

Linux で evtest で見てみたらキーボードからイベントが送られてきてないので、たぶんキーボードのハードウェアの問題。

他にも、[左Shift] を押しながら [O] [U] [N] を素早く押すと「OUUN」になることがあって、「COUNT」と入力するつもりが「COUUNT」になったりしてたんで、もともと同時押しに弱いキーボードなんだと思う。

ThinkPad トラックポイントキーボード II は「新たに点字の6点入力をサポート」ということで同時押しに強くなってそう。 前から気になってはいたんだけど、1万円切らないかなーと眺めてた。最近 ThinkPad T14 Gen3 を買ったときのポイントがあったんで、それを使って 1万円ちょっとで買えるので買ってみた。

8/6 に注文して、8/17 に届いた。

ThinkPad T14 Gen3 + Ubuntu では Bluetooth では接続できなかったけど、2.4 GHz 無線の方はちゃんと接続できた。Bluetooth はマウスも使えなかったので期待はしてなかった。たぶん Linux の問題。 ThinkPad T14 Gen3 + Ubuntu も Bluetooth でも 2.4GHz 無線でもちゃんと接続できた。Blutooth で接続できないと思ってたのは自分が接続方法を知らなかっただけだった。

無事 [無変換]+[Tab]+[左Shift] や [左SHIFT]+[O]+[U]+[N] の問題は解消した 🎉

Mac でトラックポイントでマウスカーソルを動かすと非常に遅いけど設定で一番速くしてやっと使えるレベルにはなった。

しかし Mac で [command]+[Tab] がウィンドウ単位じゃなくてアプリ単位で切り替わっちゃうのは使いづらい。ウィンドウ一つだけ前面に出そうとしてもそのアプリの全ウィンドウが前面に出たりするし、Mac はマルチウィンドウのデスクトップ環境としての使い勝手が全般的にイマイチ。

ThinkPad トラックポイントキーボード II は良いものだった。

ThinkPad T14 Gen3 のサスペンド問題

[2022-09-23 追記] BIOS アップデート(n3buj05w)で解決した! https://support.lenovo.com/jp/ja/downloads/ds557163-bios-update-utility-bootable-cd-for-windows-11-10-64-bit-thinkpad-t14-gen-3-p14s-gen-3-t16-gen-1-p16s-gen-1


前回の続き

ThinkPad T14 Gen3 の Ubuntu がサスペンドすると正常に復帰しない。

画面が消えたまま。10秒くらい待ってると点灯するけど数秒でまた消える…というのを繰り返し。 X が起動してると何もわからないので、コンソールでサスペンドを試すとこんなメッセージが出てた。

i915 0000:00:02.0: [drm] *ERROR* Failed to write source OUI
i915 0000:00:02.0: [drm] *ERROR* [ENCODER:235:DDI A/PHY A][DPRX] Failed to enable link training
i915 0000:00:02.0: [drm] *ERROR* Failed to read DPCD register 0x92
i915 0000:00:02.0: [drm] *ERROR* Failed to write source OUI
i915 0000:00:02.0: [drm] *ERROR* [ENCODER:235:DDI A/PHY A][DPRX] Failed to enable link training
i915 0000:00:02.0: [drm] *ERROR* Failed to read DPCD register 0x92
i915 0000:00:02.0: [drm] *ERROR* Failed to read DPCD register 0x92
i915 0000:00:02.0: [drm] *ERROR* Failed to write source OUI
〜以下繰り返し〜

一応、Alt + PrtSc + [R] [E] [I] [S] [U] [B]アレは効くので電源ボタンを長押ししなくても再起動は可能。

Ubuntu をセットアップしたのが 7/10 で、それ以降時間のあるときにググってみたり、Ubuntu mainlineに新しいカーネルが出てれば試してみたりしたんだけど、解決しなかった。

今日またいつものようにググってたら新しい情報をみつけた。

[ADL_P] Dual eDP support is missing, PPS state tracking gets confused, backlight does not work (#5531) · Issues · drm / intel · GitLab

Ubuntu じゃなくて Arch Linux だけど、ThinkPad T14 Gen3 Intel版でサスペンドが正常に働かずに画面がついたり消えたりする…というようなことが書かれてる。 ThinkPad T シリーズの BIOS にはバグがあって、HDMI ポートが存在しない eDP と共有しているとかなんとか(よくわかってない)。

i915 カーネルモジュールにパッチを当てて作り直せばいいっぽい。これは期待!

カーネルのバージョンが違うんで、パッチはそのままは適用できないけど、似たような処理を探してテキトーに。

i915.ko は linux-modules-extra-5.15.0-43-generic に含まれてる:

~% dpkg -S i915.ko
linux-modules-extra-5.15.0-43-generic: /lib/modules/5.15.0-43-generic/kernel/drivers/gpu/drm/i915/i915.ko

カーネルのソースをもってきて、該当ソースを改変:

~% mkdir /tmp/x
~% cd /tmp/x
/tmp/x% apt source linux-modules-extra-5.15.0-43-generic
...
/tmp/x% cd linux-5.15.0
/tmp/x/linux-5.15.0% vim ./drivers/gpu/drm/i915/display/intel_bios.c

差分。return; をコメントアウトしただけ:

/tmp/x/linux-5.15.0% diff -u ./drivers/gpu/drm/i915/display/intel_bios.c.orig ./drivers/gpu/drm/i915/display/intel_bios.c
--- ./drivers/gpu/drm/i915/display/intel_bios.c.orig  2022-07-24 17:09:36.000000000 +0900
+++ ./drivers/gpu/drm/i915/display/intel_bios.c   2022-07-24 17:11:26.940061516 +0900
@@ -1950,7 +1950,7 @@
        drm_dbg_kms(&i915->drm,
                "More than one child device for port %c in VBT, using the first.\n",
                port_name(port));
-      return;
+//     return;
    }
 
    sanitize_device_type(devdata, port);

パッケージバージョンを指定する。debian.master/changelog の先頭にテキトーに追加:

linux (5.15.0-43.99tmtms) jammy; urgency=medium

  * i915 patch

ABI check でエラーになるので無視するように設定:

/tmp/x/linux-5.15.0% touch debian.master/abi/amd64/ignore

deb パッケージを作成:

/tmp/x/linux-5.15.0% chmod +x scripts/*
/tmp/x/linux-5.15.0% debuild --no-lintian -uc -us -b
...
/tmp/x/linux-5.15.0% cd ..
/tmp/x% ls *.deb
linux-buildinfo-5.15.0-43-generic_5.15.0-43.99tmtms_amd64.deb
linux-cloud-tools-5.15.0-43-generic_5.15.0-43.99tmtms_amd64.deb
linux-cloud-tools-5.15.0-43_5.15.0-43.99tmtms_amd64.deb
linux-cloud-tools-common_5.15.0-43.99tmtms_all.deb
linux-doc_5.15.0-43.99tmtms_all.deb
linux-headers-5.15.0-43-generic_5.15.0-43.99tmtms_amd64.deb
linux-headers-5.15.0-43_5.15.0-43.99tmtms_all.deb
linux-image-unsigned-5.15.0-43-generic_5.15.0-43.99tmtms_amd64.deb
linux-libc-dev_5.15.0-43.99tmtms_amd64.deb
linux-modules-5.15.0-43-generic_5.15.0-43.99tmtms_amd64.deb
linux-modules-extra-5.15.0-43-generic_5.15.0-43.99tmtms_amd64.deb
linux-modules-iwlwifi-5.15.0-43-generic_5.15.0-43.99tmtms_amd64.deb
linux-source-5.15.0_5.15.0-43.99tmtms_all.deb
linux-tools-5.15.0-43-generic_5.15.0-43.99tmtms_amd64.deb
linux-tools-5.15.0-43_5.15.0-43.99tmtms_amd64.deb
linux-tools-common_5.15.0-43.99tmtms_all.deb
linux-tools-host_5.15.0-43.99tmtms_all.deb

一時間くらい掛かる。このうち必要なのは linux-modules-extra-5.15.0-43-generic_5.15.0-43.99tmtms_amd64.deb だけなので、これだけ作る方法はないんかな…。

インストール:

/tmp/x% sudo dpkg -i linux-modules-extra-5.15.0-43-generic_5.15.0-43.99tmtms_amd64.deb
...

Linux を再起動。

無事サスペンドできるようになった。わーい 🎉🎉🎉

本体の HDMI が使えなかったのも同じ原因だったらしく、HDMI もちゃんと使えるようになった。わーい 👏👏👏

カーネルアップデートのたびにこれをやるのは面倒だし忘れそうだから、Lenovo さんは早く BIOS を修正して欲しい…。

[2022/9/23 追記] BIOS アップデート(n3buj05w)で解決した! https://support.lenovo.com/jp/ja/downloads/ds557163-bios-update-utility-bootable-cd-for-windows-11-10-64-bit-thinkpad-t14-gen-3-p14s-gen-3-t16-gen-1-p16s-gen-1

ThinkPad T14 Gen3 の Linux 化

ThinkPad T14 Gen3 Intel版を購入

2016年10月に買った ThinkPad T460s をずっと使ってたんだけど、6年前のPCだとさすがに処理が遅く感じることもあったんで、そろそろ新しいのが欲しくなったんで、ThinkPad T14 Gen3 を買った。

安かったんでAMD版にしたんだけど、Intel版との違いはCPUだけだと思ってたのに、AMD版はメモリが増設できないことを知って、キャンセルしてIntel版に変更した。 調べてみたら、AMD版とIntel版の違いは他にも、Intel版は USB Type-C が Thunderbolt4 対応なのに AMD版は USB 3.2 というのがあった。罠っぽい。同じ機種名でこういうのはやめてほしい。

6/16 に注文して、6/28 に出荷、7/5 に届いた。

イマイチだったのが、キーボード右側のキーが正方形じゃなくて縦長なこと。なんか見覚えあるな〜と思ったら、T460s の前に使ってた X220 の前に使ってた X61 がこんな感じだった。懐かしい。X61 と違って本体の横に余裕があるんだから、もうちょっとどうにかならなかったのかなぁ…。 Lenovo のサイトに日本語キーボードの写真がどこにも載ってなかったのもアレ。

あと、カタログ上は重量 1.21kg〜 となってたんだけど実測してみたら 1.42kg あった。「〜」が書いてあるとは言え 210g は増え過ぎでは? いったい何が詰まってるんだ…。

バッテリー容量によって重量が変わるという情報もあったけど自分のは容量が小さい 39.3Wh のやつなんだよなぁ。このブログの 52.5Wh の実測 1.37kg よりも重いのはなぜ…。

まあいいや。

起動すると Windows のセットアップが始まるんだけど、Microsoft アカウントが無いと先に進めないようになってた。やめてほしい…。まあアカウント持ってるからそれで進めたんだけども。

ちょっと後で気がついたんだけど、デスクトップやドキュメントが勝手に OneDrive に保存されるようになってた。ひどい。手動で解除。

Xubuntu をインストール

Ubuntu(Xubuntu)をインストールするために、PC 起動時に F12 を押して USBメモリから Xubuntu 22.04 をブート…できない。

まあ、これはよくあることなので、BIOS の設定で Secure Boot をオフにして USB からブート…できない。BitLocker 回復パスワードを入れろとかなんとか…。

ストレージを暗号化する BitLocker というのが有効になってるらしい。

一旦 Windows を起動して、BitLocker を無効化。「設定」→「プライバシーとセキュリティ」→「デバイスの暗号化」→「デバイスの暗号化」を「オフ」。

これで Xubuntu の USBメモリからブートできるようになった。

GParted を起動して、Windows のストレージ領域を小さくする。Windows は BIOS のアップデートや周辺デバイスの動作確認とかのために残しておくのだった。

空いた領域に Xubuntu をインストールする。特に問題はなかった。

今まで使ってた T460s からの移行は /home 配下を rsync でコピー。簡単。

T460s の dpkg -l の結果を元に同じパッケージをインストール。

これで普通に使えるようになった。

問題

新し目の Windows ノート PC を Linux で使うときのあるあるなんだけど、いくつか問題が。

  • サスペンドすると正常に復帰しない。十秒おきくらいに画面が表示されるんだけどすぐ消える。
  • HDMI に外部ディスプレイをつけても認識しない。USB Type-C 接続の外部ディスプレイは認識するし、USB Type-C アダプタ経由で HDMI も使える。本体の HDMI 端子に直接繋げるとダメ。
  • Bluetooth を認識しない。

サスペンドが正常に働かないとか Bluetooth が使えないとかはまれによくあるんだけど、HDMI が使えないのはめずらしい。

サスペンドが正常に働かないのは痛い。ノート PC の意味がない…。

次回「サスペンド解決編」に続く。

LinuxでシフトJISが使えた

ふと、最近の Linux って EUC-JP ロケール使えるんだっけ? と思ったんで調べてみた。環境は Ubuntu 22.04。

EUC-JP ロケール

locales パッケージが入ってない場合はロケールはこれだけ:

% locale -a
C
C.utf8
POSIX

locales と language-pack-ja をインストール:

# apt install locales language-pack-ja

locale-gen コマンドを実行すると ja_JP.utf8 が増える:

# locale-gen
Generating locales (this might take a while)...
  ja_JP.UTF-8... done
Generation complete.

# locale -a
C
C.utf8
POSIX
ja_JP.utf8

/etc/locale.gen ファイルの ja_JP.EUC-JP をコメントアウト:

...
# it_IT@euro ISO-8859-15
# iu_CA UTF-8
ja_JP.EUC-JP EUC-JP
# ja_JP.UTF-8 UTF-8
# ka_GE GEORGIAN-PS
...

locale-gen コマンドを実行すると:

root@7a55c1aa3aba:/# locale-gen
Generating locales (this might take a while)...
  ja_JP.EUC-JP... done
  ja_JP.UTF-8... done
Generation complete.

EUC-JP ロケールが増えた:

# locale -a
C
C.utf8
POSIX
ja_JP
ja_JP.eucjp
ja_JP.ujis
ja_JP.utf8
japanese
japanese.euc

これでロケールとしては使えるようになるんだけど、端末が UTF-8 のままだと当然文字化けする。

端末のロケールを設定するには xfce4-terminal だと「設定」→「上級者」→「エンコーディング」で。

または、ロケール指定で端末を起動する。最近の端末は1プロセスで複数のウィンドウを表示してるので、別のプロセスとして起動する必要あり。

% LC_ALL=ja_JP.eucjp xfce4-terminal --disable-server

ちゃんと文字化けせずに表示された。 あいうえおa4 a2 a4 a4 a4 a6 a4 a8 a4 aa になってるので、ちゃんと EUC-JP になってることがわかる。

SJIS ロケール

locale-gen コマンドは /etc/locale.gen を使って localedef コマンドを呼び出してるので、locale-gen ではなく直接 localedef コマンドを使ってもいい:

# localedef --inputfile ja_JP --charmap EUC-JP ja_JP.EUC-JP
# locale -a
C
C.utf8
POSIX
ja_JP.eucjp

locale-gen のように、ja_JP.ujis とか japanese とかをエイリアスとして設定したい場合は、次のようにエイリアスファイルを指定する:

# localedef --inputfile ja_JP --charmap EUC-JP --alias-file /etc/locale.alias ja_JP.EUC-JP

--inputfile/usr/share/i18n/locales 内のファイル、--charmap/usr/share/i18n/charmaps 内のファイルを指定する。

/etc/locale.gen には書かれてないけど、/usr/share/i18n/charmaps/SHIFT_JIS.gz というファイルもあった。ん?実はシフトJISロケールも作れるのでは…。

試してみる:

# echo ja_JP.SJIS SHIFT_JIS >> /etc/locale.gen
# locale-gen
Generating locales (this might take a while)...
  ja_JP.SHIFT_JIS...[warning] character map `SHIFT_JIS' is not ASCII compatible, locale not ISO C compliant [--no-warnings=ascii]
 done
Generation complete.
# locale -a
C
C.utf8
POSIX
ja_JP.sjis
japanese.sjis

警告は出るけど作れたっぽい。

xfce4-terminal を EUC-JP のときと同じようにしてエンコーディングを「SHIFT_JIS」で起動。

ちゃんとシフトJISになった。

端末の中で vim を起動してもちゃんと日本語編集できた。

MySQL もちゃんと使えた。

Linux はシフトJIS使えたんだなー。今まで知らなかったわ…。

rkremap: キーボードデバイスの自動検出

https://blog.tmtms.net/entry/202201/rkremap の続き。

blog.tmtms.net

Rkremap.new 時に引数でキーボードデバイスファイルを指定しないといけなかったんだけど、USB や Bluetooth キーボードとかデバイスファイル名がわからない場合に調べるのが面倒なので自動検出するようにしてみた。

入力デバイスの種類の取得

/dev/input/event* に対して ioctl(EVIOCGBIT(0)) をすれば入力デバイスの種類が得られる。

こんな感じ:

#include <linux/input.h>
#include <stdio.h>
#include <fcntl.h>

int main(int argc, char *argv[])
{
  unsigned char type[(EV_MAX-1)/8+1];
  int fd = open("/dev/input/event3", O_RDONLY);
  ioctl(fd, EVIOCGBIT(0, sizeof(type)), type);
  for (int i=0; i<sizeof(type); i++) {
    printf("%02x ", type[i]);
  }
  puts("");
}

この typeEV_* ビット目が立ってればその種類のデバイスということになるらしい。 EV_* 定数は /usr/include/linux/input-event-codes.h に定義されている。

ThinkPad T460s 本体のキーボード(/dev/input/event3)だと 13 00 12 00 という出力になった。 並び替えて2進数で表すと 0000 0000 0001 0010 0000 0000 0001 0011 となり、対応するビットは、0, 1, 4, 17, 20 なので、EV_SYN, EV_KEY, EV_MSC, EV_LED, EV_REP となる。

EV_KEY が含まれてるので、キーを持つデバイスだということがわかる。

Ruby で同じようなことをするにはこんな感じ:

EV_KEY = 1
EVIOCGBIT_0 = 2147763488     # EVIOCGBIT(0, 4)
f = File.open('/dev/input/event3')
buf = ''
f.ioctl(EVIOCGBIT_0, buf)
p buf[0, 4].unpack('C*').map{'%02X'%_1}.join(' ')

キーデバイスがキーボードかどうかの判定

ただ、この EV_KEY はキーボードだけじゃなくてキー(ボタン?)を持つデバイス全般が該当するらしい。 ThinkPad で evtest コマンドで調べると EV_KEY はほかにもあって、たとえば event1KEY_SLEEP だけを持つ Sleep Button で、event2KEY_POWER だけを持つ Power Button、タッチパッド(event15 Synaptics TM3145-003) やトラックポイント(event16 TPPS/2 IBM TrackPoint)も EV_KEY だった。

rkremap の対象は普通のキーボードデバイスなので、0, 9, A, Z, SPACE キーがあるデバイスを対象にした。

キーデバイスが対応しているキーを調べるには、ioctl(EVIOCGBIT(EV_KEY)) で得られた値に対してデバイスに対応する KEY_* ビットが立っているかどうかで判定できる。

#include <linux/input.h>
#include <stdio.h>
#include <fcntl.h>

int capable(unsigned char key[], int code)
{
  return (key[code/8] >> (code%8)) & 1;
}

int main(int argc, char *argv[])
{
  unsigned char key[(KEY_MAX-1)/8+1];
  int fd = open("/dev/input/event3", O_RDONLY);
  ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(key)), key);
  printf("KEY_0=%d\n", capable(key, KEY_0));
  printf("KEY_9=%d\n", capable(key, KEY_9));
  printf("KEY_A=%d\n", capable(key, KEY_A));
  printf("KEY_Z=%d\n", capable(key, KEY_Z));
  printf("KEY_SPACE=%d\n", capable(key, KEY_SPACE));
}

Ruby だとこんな感じ:

EVIOCGBIT_EV_KEY = 2153792801  # EVIOCGBIT(0, 96)
EV_KEY = 1
KEY_0 = 11
KEY_9 = 10
KEY_A = 30
KEY_Z = 44
KEY_SPACE = 57
def capable?(code)= @key[code/8][code%8] != 0
f = File.open('/dev/input/event3')
buf = ''
f.ioctl(EVIOCGBIT_EV_KEY, buf)
@key = buf.unpack('C*')
puts "KEY_0=#{capable? KEY_0}"
puts "KEY_9=#{capable? KEY_9}"
puts "KEY_A=#{capable? KEY_A}"
puts "KEY_Z=#{capable? KEY_Z}"
puts "KEY_SPACE=#{capable? KEY_SPACE}"

/dev/input/event* を一つずつ上記のように調べてキーボードかどうかを調べることができる。

キーボードデバイスが複数の場合もあるので、IO.select で複数の入力を待つようにした。これで、本体のキーボードでCtrlキーを押しながら、USBキーボードでAキーを押すみたいなのにも対応できた。

参考にしたもの等


現状では rkremap 起動後にキーボードデバイスが追加/削除されたのには対応できないもどうにかしたいなぁ…。

Linux用キーリマッパー rkremap を作った

11月から仕事で Mac を使うようになって2ヶ月ちょっとたつけど、いまだにショートカットキーが Ctrl キーではなく Command キーであることに慣れない。

慣れないのは仕事以外で普段使ってる Linux と異なるからだと思うんだけど、普通に考えて Mac のショートカットキーの方が合理的だと思うので、Linux 上で Mac と同じような操作ができるようにした方が良いと思った。

というわけで Ruby で rkremap というのを作ってみた。rkremap はツールではなくライブラリなので、rkremap を使ったプログラムを作る必要がある。

github.com

まあ普通は「最強のキーリマッパー」の xremap を使うのがいいと思う。

作ろうと思ったのは xremap では(たぶんほかのツールも)日本語変換有効時を特別扱いできなかったのが発端なんだけど、YAML 等の設定ファイルを書くよりもプログラムで動きを書きたかったのでライブラリとして実装してみた。

以下、rkremap の使い方と背景の仕組みなど。

キーボード入力デバイスファイルを調べる

デバイスファイルは /dev/input/event[0-9]*。 キーボードに対応するデバイスは cat /proc/bus/input/devices でわかるかもしれない。 ThinkPad 本体のキーボードは event3 だった。

I: Bus=0011 Vendor=0001 Product=0001 Version=ab54
N: Name="AT Translated Set 2 keyboard"
P: Phys=isa0060/serio0/input0
S: Sysfs=/devices/platform/i8042/serio0/input/input3
U: Uniq=
H: Handlers=sysrq kbd event3 leds 
B: PROP=0
B: EV=120013
B: KEY=402000000 3803078f800d001 feffffdfffefffff fffffffffffffffe
B: MSC=10
B: LED=7

sudo evtest /dev/input/event3 のようにしてキーを押して反応があればそのデバイスであることがわかる。

キーロガー

/dev/input/eventX ファイルを読むことでそのデバイスのキーイベントを読み取れる。 一般ユーザーには読み取る権限がないので rootinput グループに所属しているユーザーである必要がある。

詳しくは 1.1. Introduction — The Linux Kernel documentation を。

rkremap を使うとこんな感じでキー入力を読める:

require 'rkremap'
def code2key(code)
  Rkremap::CODE_KEY[code].to_s.sub(/\AKEY_/, '')
end
rk = Rkremap.new('/dev/input/event3')
rk.start do |code, mod|
  key = (mod.select{|_, v| v}.keys + [code]).map{|c| code2key(c)}.join('-')
  puts key
end

この場合はキーイベントは本来のアプリに渡されるのでアプリに影響を与えることなく、キー入力を読み取ることができる。

start のブロックは修飾キー以外のキーが押されたときに実行される。 ブロックの第1引数は押されたキーのコード(Integer)。Rkremap::CODE_KEY でシンボルに変換できる。 第2引数は修飾キーの Hash

「hoge!」と入力したときの出力:

H
O
G
E
LEFTSHIFT-1

通常使用しているユーザーを input グループに所属させると sudo とかの手間はなくていいんだけど、簡単にキーロガーを動かせちゃうので、セキュリティ的にはちょっと考えた方がいいかも。

Xアプリ名の取得

特定のアプリでだけ特殊な処理を行いたいとかで入力フォーカスがあるアプリの名前を知りたいことがある。

X11 の XGetInputFocus() でフォーカスしてる Window を得られる。 この Window はアプリのウィンドウではなくてアプリを構成している部品のようなもので、アプリのウィンドウを得るには、XGetClassHint() でクラス名を得られるまで XQueryTree() で親 Window を辿る。

…ということを Rkremap::App(Rkremap#start のブロックの第3引数)でできるようにしてある。ウィンドウタイトルは #title, クラス名は #class_name で得られる。

Ruby から使える X11 ライブラリにいいのが見つからなかったので、ffi を使って実装した。X11 みたいな巨大なライブラリの一部をつまみ食いするには ffi は便利。

この機能を使用するには Rkremap#x11 = true を設定する。

上のキーロガーの出力にアプリ名を追加するにはこんな感じ:

require 'rkremap'
def code2key(code)
  Rkremap::CODE_KEY[code].to_s.sub(/\AKEY_/, '')
end
rk = Rkremap.new('/dev/input/event3')
rk.x11 = true
rk.start do |code, mod, app|
  key = (mod.select{|_, v| v}.keys + [code]).map{|c| code2key(c)}.join('-')
  key << " at #{app.title} [#{app.class_name}]" if rk.x11
  puts key
end

Firefox 上の Google で「ほげ」を検索したときの出力:

HENKAN at Google — Mozilla Firefox [Firefox]
H at Google — Mozilla Firefox [Firefox]
O at Google — Mozilla Firefox [Firefox]
G at Google — Mozilla Firefox [Firefox]
E at Google — Mozilla Firefox [Firefox]
ENTER at Google — Mozilla Firefox [Firefox]
MUHENKAN at Google — Mozilla Firefox [Firefox]

仮想キーボードデバイス

/dev/uinput を使って仮想キーボードデバイスを作ることができる。

ioctl(UI_SET_EVBIT, EV_KEY) でキーボードデバイスを指定し、ioctl(UI_SET_KEYBIT) で入力可能なキーを指定し、ioctl(UI_DEV_SETUP) でデバイスのベンダーや製品バージョンを指定して、ioctl(UI_DEV_CREATE) で作るって感じ。

作った仮想デバイスに対してイベントデータを書き込むことでキーを入力したことにできる。

詳しくは 1.7. uinput module — The Linux Kernel documentation を。

rkremap は Rkremap#key でキーを押したことにできる。

以下は1秒毎に A〜Z のキーを入力する:

require 'rkremap'
rk = Rkremap.new("/dev/input/event3")
while true
  ('A'..'Z').each do |k|
    key = Rkremap::KeyCode.const_get("KEY_#{k}")
    rk.key(key)
    sleep 1
  end
end

キーリマップ

/dev/input/eventX に対して ioctl(EVIOCGRAB, 1) を設定するとキー入力イベントを奪い取ってアプリに入力が渡らなくなる。

そして読み取ったキーイベントに対して何かしらの処理後に /dev/uinput に書くことでキーイベントを書き換えることができる。

rkremap で、Firefox 上での Ctrl-N に置き換えるにはこんな感じ:

require 'rkremap'
include Rkremap::KeyCode
rk = Rkremap.new("/dev/input/event3")
rk.grab = true
rk.x11 = true
rk.start do |code, mod, app|
  if app.class_name == 'Firefox' &&  code == KEY_N && (mod[KEY_LEFTCTRL] || mod[KEY_RIGHTCTRL])
    mod[KEY_LEFTCTRL] = mod[KEY_RIGHTCTRL] = false
    code = KEY_DOWN
  end
  rk.key(code, mod)
end

ということで自分が使うために作ったプログラムはこちら https://github.com/tmtm/rkremap/blob/master/example/tmtms.rb

元々の目的だった Mac 風のショートカットにするために、Alt+英字/EnterCtrl に変換してるほか、CapsLockCtrl として扱ったり、変換/無変換 を長押しすると Alt として扱ったり、日本語キーボードを英語キーボードとして認識させたときのキー配置を入れ替えてたりなどしている。

とりあえず動くところまでできたので、これでリリース。

参考にしたもの等

Linux で X よりも低レイヤでキーマップを変更する

まとめ

USB 接続の ThinkPad トラックポイントキーボードで、半角/全角EscCapsLockCtrl として使うには、/etc/udev/hwdb.d/10-tmtms.hwdb というファイル(ファイル名は何でもいい)を次の内容で作って:

evdev:name:Lenovo ThinkPad Compact USB Keyboard with TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*
 KEYBOARD_KEY_70035=esc
 KEYBOARD_KEY_70039=leftctrl

次のコマンドを実行する:

% sudo udevadm hwdb --update
% sudo udevadm trigger

以下は調査とかなので読まなくてもいい

30年くらい Emacs を使ってたんだけど、そろそろ VSCode を使ってみようかーと思って使ってみた。 デフォルトではカーソル移動がカーソルキーだけでショートカットキーが用意されてないっぽくて、使い始めくらいはデフォルト設定のまま使ってみようと思ってたんだけど、さすがにカーソルキーを使うのは厳しいので、結局 Awesome Emacs Keymap というのを入れた。

qiita.com

Use Meta Prefix Escape という設定を有効にすれば M-Esc キーを使うこともできる。

が、うまく動かなかった。元々の物理キーの Esc であれば効くんだけど、xmodmap や XKB で設定した Esc だと効かない(1 の左隣の 半角/全角Esc に変更してる)。

tmtms.hatenablog.com

xev で調べてもちゃんと keysym は Escape になってる:

KeyPress event, serial 37, synthetic NO, window 0x1800001,
    root 0x7c7, subw 0x0, time 906346, (72,68), root:(3823,1069),
    state 0x0, keycode 49 (keysym 0xff1b, Escape), same_screen YES,
    XKeysymToKeycode returns keycode: 9
    XLookupString gives 1 bytes: (1b) "
mbLookupString gives 1 bytes: (1b) "
FilterEvent returns: False

ということは keysym じゃなくて keycode を使ってるのかもしれない。

X レベルではどうしようもなさそうなので、それよりも低いレイヤで keycode を変更できないかとググってみたら、setkeycodes というのでできるらしい。 どうやらキーボードからの物理的なコード(スキャンコード)をキーコードに変換するマップを変更できるらしい。

X 上じゃ駄目で、コンソールでやる必要があるみたいなので、Ctrl-Alt-1 とかでコンソールに移ってから実行する(Alt-7 とかで X に戻れる)。

showkey -s半角/全角 のスキャンコードを調べる:

% showkey -s
kb mode was UNICODE
[ if you are trying this under X, it might not work
since the X server is also reading /dev/console ]

press any key (program terminates 10s after last keypress)...
0x29 0xa9

Ctrl-C では止まらない。10秒以上放置すると終了する。

showkey -kEsc のキーコードを調べる:

% showkey -s
kb mode was UNICODE
[ if you are trying this under X, it might not work
since the X server is also reading /dev/console ]

press any key (program terminates 10s after last keypress)...
keycode   1 press
keycode   1 release

スキャンコード 0x29 をキーコード 1 に変換すればいいらしいので、setkeycodes を実行:

% sudo setkeycodes 0x29 1

これでノートPCの本体キーボードの方はちゃんと変わったんだけど、USBキーボードの方は効いてない。

man page には次のように書いてあった。(Ubuntu の man setkeycodes にはこの記述は無かった)

setkeycodes affects only the "first" input device that has modifiable scancode-to-keycode mapping. If there is more than one such device, setkeycodes cannot change the mapping of other devices than the "first" one.

ということで、USB の ThinkPad トラックポイントキーボードを使ってる自分は別の方法を考える必要があるのだった。

またググってみたら、こんなページを見つけた。

wiki.archlinux.jp

どうやら udev を使えばできるらしい。

evdev:name:<input device name>:dmi:bvn*:bvr*:bd*:svn<vendor>:pn*
 KEYBOARD_KEY_<scancode>=<keycode>

という形式で指定すれば良さそう。

<input device name> はたぶん xinput の名前を使えば良さそう:

% xinput
⎡ Virtual core pointer                        id=2    [master pointer  (3)]
⎜   ↳ Virtual core XTEST pointer                id=4    [slave  pointer  (2)]
⎜   ↳ TPPS/2 IBM TrackPoint                     id=16   [slave  pointer  (2)]
⎜   ↳ Lenovo ThinkPad Compact USB Keyboard with TrackPoint  id=10   [slave  pointer  (2)]
⎜   ↳ Synaptics TM3145-003                      id=15   [slave  pointer  (2)]
⎣ Virtual core keyboard                       id=3    [master keyboard (2)]
    ↳ Virtual core XTEST keyboard                 id=5    [slave  keyboard (3)]
    ↳ Power Button                                id=6    [slave  keyboard (3)]
    ↳ Video Bus                                   id=7    [slave  keyboard (3)]
    ↳ Sleep Button                                id=8    [slave  keyboard (3)]
    ↳ Generic USB2.0 Microphone                   id=11   [slave  keyboard (3)]
    ↳ Integrated Camera: Integrated C             id=12   [slave  keyboard (3)]
    ↳ AT Translated Set 2 keyboard                id=13   [slave  keyboard (3)]
    ↳ ThinkPad Extra Buttons                      id=14   [slave  keyboard (3)]
    ↳ Lenovo ThinkPad Compact USB Keyboard with TrackPoint    id=9    [slave  keyboard (3)]
    ↳ Lenovo ThinkPad Compact USB Keyboard with TrackPoint    id=17   [slave  keyboard (3)]

Lenovo ThinkPad Compact USB Keyboard with TrackPoint という名前らしい。

<vendor> はよくわからなかったけど、たぶん LENOVO* でもいいのかもしれない。

<scancode> の調べ方は evtest を使えばいいらしい。 wiki.archlinux.jp

入ってなかったので sudo apt insttall evtest でインストール。

引数の /dev/input/eventXX はこんな感じで調べられた:

% ls -l /dev/input/by-path/*usb*kbd
lrwxrwxrwx 1 root root 9  9月 24 12:15 /dev/input/by-path/pci-0000:00:14.0-usb-0:3:1.0-event-kbd -> ../event5

evtest を起動して 半角/全角 キーと Esc を押す:

% sudo evtest /dev/input/event5
...
Testing ... (interrupt to exit)
Event: time 1632465959.559163, type 4 (EV_MSC), code 4 (MSC_SCAN), value 70035
Event: time 1632465959.559163, type 1 (EV_KEY), code 41 (KEY_GRAVE), value 1
Event: time 1632465959.559163, -------------- SYN_REPORT ------------
Event: time 1632465959.606785, type 4 (EV_MSC), code 4 (MSC_SCAN), value 70035
Event: time 1632465959.606785, type 1 (EV_KEY), code 41 (KEY_GRAVE), value 0
Event: time 1632465959.606785, -------------- SYN_REPORT ------------

Event: time 1632465965.087102, type 4 (EV_MSC), code 4 (MSC_SCAN), value 70029
Event: time 1632465965.087102, type 1 (EV_KEY), code 1 (KEY_ESC), value 1
Event: time 1632465965.087102, -------------- SYN_REPORT ------------
Event: time 1632465965.126598, type 4 (EV_MSC), code 4 (MSC_SCAN), value 70029
Event: time 1632465965.126598, type 1 (EV_KEY), code 1 (KEY_ESC), value 0
Event: time 1632465965.126598, -------------- SYN_REPORT ------------

半角/全角 のスキャンコードは 70035 で、Esc のキーコードは ESC らしい。 (半角/全角 なのに GRAVE と表示されてるのは気にしなくていい。日本語キーボードなのに英語配列を選択してるためなので。)

Esc だけじゃなくて、ついでに CapsLock を左Ctrl に変更する方法も同様にして調べた。 Capslock のスキャンコードは 70039 で、左Ctrl のキーコードは LEFTCTRL だった。

/etc/udev/hwdb.d/10-tmtms.hwdb というファイルを作る(ファイル名は何でもいい):

evdev:name:Lenovo ThinkPad Compact USB Keyboard with TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*
 KEYBOARD_KEY_70035=esc
 KEYBOARD_KEY_70039=leftctrl

キーコードは小文字で書かないといけないらしい。

データベースファイルを更新:

% sudo udevadm hwdb --update

反映:

% sudo udevadm trigger

確認:

% udevadm info /dev/input/event5 | grep KEYBOARD_KEY
E: KEYBOARD_KEY_70035=esc
E: KEYBOARD_KEY_70039=leftctrl

ということでちゃんと VSCode でも 半角/全角Esc として使えるようになった。この記事も VSCode で書いてみた。(まだ慣れない)

sudo su とかしてる人はだいたいおっさん

テキトーに書いたこれが「ややウケ」だった。

そういや自分が sudo 使い始めたのは20年以上前で、うろ覚えなので調べてみた。

まとめ

カレントディレクトリ 引き継がれる環境変数 シェル ログインシェル
su 変わらない ほぼすべて デフォルトのシェル No
su - ユーザーのホーム TERM のみ デフォルトのシェル Yes
sudo su 変わらない 一部 デフォルトのシェル No
sudo su - ユーザーのホーム TERM のみ デフォルトのシェル Yes
sudo -s 変わらない 一部 実行時の $SHELL No
sudo -i ユーザーのホーム 一部 デフォルトのシェル Yes

sudo susudo -s はほぼ同じ。実行されるシェルが異なることがある。

sudo su -sudo -i もほぼ同じ。環境変数のクリア的な意味だと sudo su - の方が強い。

以下は別に読まなくてもいい。

su

別のユーザーでシェルを実行するコマンド。自分は「す」とか「えすゆー」とかと呼んでる。 元は super user とか switch user とか substitute user の略だったらしい。

デフォルトでは root になるが、引数でユーザー名を指定するとそのユーザーになる。 新ユーザーのデフォルトのシェルとして設定されているシェルが実行される。

入力するパスワードは新ユーザーのパスワード。

~% su
Password: (rootのパスワード)
root@hostname:/home/tmtms# id
uid=0(root) gid=0(root) groups=0(root)
root@hostname:/home/tmtms# 
~% su hoge
Password: (hogeのパスワード)
hoge@hostname:/home/tmtms$ id
uid=1001(hoge) gid=1001(hoge) groups=1001(hoge)
hoge@hostname:/home/tmtms$ 

ユーザー名につづけて引数を追加するとシェルに渡される。

~% su hoge -c id
Password: (hogeのパスワード)
uid=1001(hoge) gid=1001(hoge) groups=1001(hoge)
~% 

カレントディレクトリは変わらない。

シェルが bash の場合は /etc/bash.bashrc~/.bashrc を実行するが、ログインシェルではないので、/etc/profile~/.profile は実行されない。

環境変数は HOME, SHELL が設定される。root 以外のユーザーになる場合は USER, LOGNAME もセットされる。それ以外の環境変数は引き継がれる。

Ubuntu で実際に su を試してみたらさらに PATH, MAIL も変更された。

PATH/etc/login.defs に次のような設定があるんだけど、値が違うんでこれではなさそう。

#
# *REQUIRED*  The default PATH settings, for superuser and normal users.
#
# (they are minimal, add the rest in the shell startup files)
ENV_SUPATH      PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
ENV_PATH        PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games

/etc/pam.d/su/etc/environment に次のように書かれてて、どうやらこれが効いてるっぽい。

# This module parses environment configuration file(s)
# and also allows you to use an extended config
# file /etc/security/pam_env.conf.
# 
# parsing /etc/environment needs "readenv=1"
session       required   pam_env.so readenv=1
# locale variables are also kept into /etc/default/locale in etch
# reading this file *in addition to /etc/environment* does not hurt
session       required   pam_env.so readenv=1 envfile=/etc/default/locale
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin"

MAIL/etc/pam.d/su のこれが効いてる。たぶん。

# Defines the MAIL environment variable
# However, userdel also needs MAIL_DIR and MAIL_FILE variables
# in /etc/login.defs to make sure that removing a user 
# also removes the user's mail spool file.
# See comments in /etc/login.defs
#
# "nopen" stands to avoid reporting new mail when su'ing to another user
session    optional   pam_mail.so nopen

su -

su と同じく新ユーザーでシェルが実行されるんだけど、ログインシェルとして実行される。

カレントディレクトリも新ユーザーのホームディレクトリになる。

シェルが bash の場合は /etc/profile, ~/.bash_profile, ~/.bash_login, ~/.profile などが実行される。

環境変数はクリアされるが、ひとつだけ例外があって TERM 環境変数だけは引き継がれる。TERM までクリアされちゃうと端末制御がおかしくなってしまうので…。

sudo

別のユーザーでコマンドを実行するためのコマンド。自分は「すーどぅー」と呼んでいる。 su がシェルを実行するのに対して sudo は引数で実行するコマンドを指定する。

入力するパスワードは元のユーザー(sudo を実行したユーザー)のパスワード。

~% sudo id
[sudo] password for tmtms: (元のユーザーのパスワード)
uid=0(root) gid=0(root) groups=0(root)
~% 

デフォルトでは root になるが、-u オプションでユーザーを指定できる。

~% sudo -u hoge id
[sudo] password for tmtms: (元のユーザーのパスワード)
uid=1001(hoge) gid=1001(hoge) groups=1001(hoge)
~% 

カレントディレクトリは変わらない。

環境変数はいろいろ引き継がれる(後述)。

自分のパスワードを入力すればいいので、他のユーザー、とくに root のパスワードを知らなくても root でコマンドを実行できるのが便利。

といっても誰でも root でコマンドを実行できちゃ困るんで、/etc/sudoers で sudo を使える人を制限している。 Ubuntu の場合は admin グループや sudo グループに属しているユーザーが sudo を使用できる。

Ubuntu は root にはパスワードは設定されないので su は使えない。root での作業が必要な場合は sudo を使うことになる。

sudo に一度パスワードを入力すると、一定時間(デフォルトでは15分間)パスワードなしで実行できるので、連続して sudo を実行するときにもいちいちパスワードが聞かれなくて便利。sudo -K で時間をリセットすることができる。

どうせ聞かれると思って先走ってパスワードを入力すると、まだ前回実行時から15分経ってなくて、パスワード文字列が端末に表示されるという事故もときどき…。

sudo -s

root でコマンドを実行したいときに sudo をつけるだけで実行できるんでいいんだけど、root でシェルの機能(ワイルドカードとかリダイレクトとか)を使いたいこともあって、sudo で対話型シェルを実行したい。

そのときに例の sudo su が使われることがある。

sudo su は root 権限で su を実行し、su は root でシェルを実行するので、結果的に sudo を使って root でシェルを起動することになる。

~% sudo su
[sudo] password for tmtms: (元のユーザーのパスワード)
root@hostname:/home/tmtms# 

まあ別にいいんだけど、それなら sudo bash の方がシンプルな気がする。タイプ数が多いけど。

~% sudo bash
[sudo] password for tmtms: (元のユーザーのパスワード)
root@hostname:/home/tmtms# 

で、sudo には -s というオプションがあって、シェルを起動することができる。sudo su よりもこっちの方が良さそう。

ただし、このとき起動されるシェルは su と違って sudo 実行時の SHELL 環境変数の値が使用される。

~% sudo -s
[sudo] password for tmtms: (元のユーザーのパスワード)
hostname# 

sudo -i

sudo には -i というオプションもあって、これは su - と同じようにシェルがログインシェルとして起動される。 シェルはちゃんと新ユーザーのデフォルトシェルで、カレントディレクトリも新ユーザーのホームディレクトリになる。

% sudo -i
[sudo] password for tmtms: (元のユーザーのパスワード)
root@hostname:~# 

sudo の環境変数

root で sudo -V を実行すると sudo の設定がいろいろ表示される。引き継がれる環境変数もここで出力される。

~% sudo -V
Sudo バージョン 1.9.5p2
sudoers ポリシープラグイン  バージョン 1.9.5p2
sudoers ファイル文法バージョン 48
Sudoers I/O plugin version 1.9.5p2
Sudoers audit plugin version 1.9.5p2
~% sudo sudo -V 
Sudo バージョン 1.9.5p2
configure オプション: --build=x86_64-linux-gnu --prefix=/usr --includedir=${prefix}/include --mandir=${prefix}/share/man --infodir=${prefix}/share/info --sysconfdir=/etc --localstatedir=/var --disable-option-checking --disable-silent-rules --libdir=${prefix}/lib/x86_64-linux-gnu --libexecdir=${prefix}/lib/x86_64-linux-gnu --disable-maintainer-mode --disable-dependency-tracking -v --with-all-insults --with-pam --with-fqdn --with-logging=syslog --with-logfac=authpriv --with-env-editor --with-editor=/usr/bin/editor --with-exampledir=/usr/share/doc/sudo/examples --with-timeout=15 --with-password-timeout=0 --with-passprompt=[sudo] password for %p:  --without-lecture --with-tty-tickets --disable-root-mailer --enable-admin-flag --with-sendmail=/usr/sbin/sendmail --with-rundir=/run/sudo --libexecdir=/usr/lib --with-sssd --with-sssd-lib=/usr/lib/x86_64-linux-gnu --enable-zlib=system --with-selinux --with-linux-audit --enable-tmpfiles.d=yes MVPROG=/bin/mv
sudoers ポリシープラグイン  バージョン 1.9.5p2
sudoers ファイル文法バージョン 48

sudoers のパス: /etc/sudoers
認証方法: 'pam'
ログ記録時に syslog を使用する場合の syslog ファシリティ: authpriv
ログ記録時に syslog を使用する場合の syslog プライオリティ: notice
ユーザー認証に失敗したと時に使用される syslog プライオリティ: alert
ユーザー認証に失敗した場合にメールを送信します
ユーザー他 sudoers 内に存在しない場合にメールを送信します
ユーザーが最初に sudo を実行した時に講義を行う
デフォルトでユーザーが認証されていることを必要とします
root が sudo を実行するかもしれません
役に立つエラーメッセージを表示するためにいくつかの情報を収集することを許可します
sudoers ファイルに完全修飾ホスト名 (FQDN) を要求します
visudo が EDITOR 環境変数を尊重して使用します
LOGNAME および USER 環境変数を設定します
ログファイルの行頭から改行までの長さ (0 の場合は改行しない): 80
認証タイムスタンプのタイムアウト値: 15.0 分
パスワード入力要求のタイムアウト値: 0.0 分
パスワード入力の試行回数: 3
使用する umask 値 (0777 の場合はユーザーの設定値を使用します): 022
メールプログラムのパス: /usr/sbin/sendmail
メールプログラムの引数フラグ: -t
メールの送信先アドレス: root
メールの件名 (Subject) 行: *** SECURITY information for %h ***
パスワードを間違った時のメッセージ: すみません、もう一度試してください。
受講状況ディレクトリのパス: /var/lib/sudo/lectured
認証タイムスタンプディレクトリのパス: /run/sudo/ts
パスワード入力要求時に表示される文字列: [sudo] %p のパスワード: 
コマンドを実行するデフォルトの変更先ユーザー: root
ユーザーの $PATH を上書きする時の値: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin
visudo で使用されるエディターのパス: /usr/bin/editor
'list' 疑似コマンド使用するためにパスワードを要求される時: any
'verify' 疑似コマンドを使用するためにパスワードを要求される時: all
3 以上の値をもつファイル記述子をコマンド実行前に閉じます
環境変数の集合をデフォルトに設定します
安全性の確認を行う環境変数:
    TZ
    TERM
    LINGUAS
    LC_*
    LANGUAGE
    LANG
    COLORTERM
削除する環境変数:
    *=()*
    RUBYOPT
    RUBYLIB
    PYTHONUSERBASE
    PYTHONINSPECT
    PYTHONPATH
    PYTHONHOME
    TMPPREFIX
    ZDOTDIR
    READNULLCMD
    NULLCMD
    FPATH
    PERL5DB
    PERL5OPT
    PERL5LIB
    PERLLIB
    PERLIO_DEBUG
    JAVA_TOOL_OPTIONS
    SHELLOPTS
    BASHOPTS
    GLOBIGNORE
    PS4
    BASH_ENV
    ENV
    TERMCAP
    TERMPATH
    TERMINFO_DIRS
    TERMINFO
    _RLD*
    LD_*
    PATH_LOCALE
    NLSPATH
    HOSTALIASES
    RES_OPTIONS
    LOCALDOMAIN
    CDPATH
    IFS
保護する環境変数:
    XAUTHORIZATION
    XAUTHORITY
    PS2
    PS1
    PATH
    LS_COLORS
    KRB5CCNAME
    HOSTNAME
    DPKG_COLORS
    DISPLAY
    COLORS
sudoers を構文解析する時に使用するロケール: C
I/O ログを zlib を使用して圧縮します
入出力 (I/O) ログを保存するディレクトリです:/var/log/sudo-io
入出力 (I/O) ログを保存するファイルです:%{seq}
pty を割り当てた時に utmp/utmpx ファイルに記録を加えます
利用する PAM サービス名: sudo
ログインシェルで利用する PAM サービス名: sudo
ターゲットユーザーの PAM 資格情報による認証を試みる
実行するコマンドのために新しい PAM セッションを生成する
PAM アカウント検証管理を実行しています
sudoers のネットグループサポートを有効にする
ファイルを sudoedit で編集するときに親ディレクトリが書き込み可能か確かめる
監査ログファイルへの書き込みができなくても、コマンドの実行を許可する
ログファイルへの書き込みができなくても、コマンドの実行を許可する
ログエントリーがこの値より長くなると、複数の syslog メッセージに分割されます: 960
I/O ログのファイルモード: 0600
コマンドの実行時にパスでなくファイル記述子を使う: digest_only
認証タイムスタンプのタイプ: tty
ユーザー名の検索で大文字小文字を同一視する
グループ名の検索で大文字小文字を同一視する
コマンドが sudoers で許可された場合にログに記録します
コマンドが sudoers で拒否された場合にログに記録します
Sudo ログサーバーのタイムアウト、単位は秒: 30
ログサーバーに接続したソケットで SO_KEEPALIVE ソケットオプションを有効にする
ログサーバーの証明書が有効か検証する
PAMのリモートユーザーを sudo を実行しているユーザーに設定
生成するログの書式: sudo
Enable SELinux RBAC support

ローカル IP アドレスとネットマスクの組:
    xxx.xxx.x.xxx/xxx.xxx.xxx.x
    xxx.xxx.x.xxx/xxx.xxx.xxx.x
    xxx.xx.x.x/xxx.xxx.x.x
    xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxx/xxxx:xxxx:xxxx:xxxx::
    xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx/xxxx:xxxx:xxxx:xxxx::
    xxxx::xxxx:xxxx:xxxx:xxxx/xxxx:xxxx:xxxx:xxxx::
    xxxx:xxxx:xxxx:xxxx:xxxx:xxx:xxxx:xxxx/xxxx:xxxx:xxxx:xxxx::
    xxxx::xxxx:xxxx:xxxx:xxxx/xxxx:xxxx:xxxx:xxxx::

Sudoers I/O plugin version 1.9.5p2
Sudoers audit plugin version 1.9.5p2

AndroidスマホをLinux PCのカメラにする

自分の作業机は正面に27インチディスプレイを置いてノートPCはその脇に置いてるんだけど、Zoom とかのオンラインミーティングで顔出しする時にPCのカメラだと斜めからの映像になって良くないなーと思ってて、Webカメラつけるのもいいかと思ったんだけど今品薄だし、そういえばAndroidスマホをPCのカメラにできるというのをどっかで読んだ気がする。

…ということで、テキトーに「linux android webカメラ」とかでググると、ケータイWatchの記事が見つかった。

k-tai.watch.impress.co.jp

DroidCam という Android アプリを使えばできるらしい。

試してみたらできたんだけど、

  • フロントカメラが使えない
  • adb で繋ごうとすると Linux のクライアントアプリが落ちる

…ということでやめ。

Google Play で「linux webcam」で検索すると「IP Webcam」というのが見つかった。

play.google.com

起動すると設定画面が出てきて、一番下の「映像ストリーミング開始」をタップするとカメラが起動する。

カメラが動くと IPv4: https://192.168.10.100:8080 のように表示されてるので、ブラウザでそのURLにアクセスするとこんな画面が表示される。

f:id:tmtms:20200518014137p:plain

ブラウザでいろいろコントロールできる。結構高機能。

上の「ビデオレンダリング」の「JavaScript」とかを押すとブラウザ上にカメラ映像が表示される。

f:id:tmtms:20200518014922p:plain

で、肝心の PC のカメラにする方法は、ブラウザ画面上部の「ビデオチャットドライバー」から辿れる。Linux の場合は GitHub。

github.com

ドライバーと言ってもただの bash スクリプトで、変なアプリをインストールさせられるわけではない。

スマホが USB ケーブルでつながっていて Linux に adb がインストールされていれば adb を使用して通信するように動く。 ただし、スマホの「開発者向けオプション」で「USBデバッグ」を有効にしておく必要あり。

そうでなければネットワークで繋ごうとするので、スクリプト中の WIFI_IPPORT をスマホに表示されているものに書き換えておいた方がいい。 あと、WIDTHHEIGHT の比率がスマホの画面と合ってないと映像が太ったり痩せたりするので合わせておく。

GST_VIDEO_CONVERTER="ffmpegcolorspace" という行があるが、これは最近は videoconvert というものに変わったらしいので書き換えておく。 (https://stackoverflow.com/questions/44780991/no-element-ffmpegcolorspace-in-gstreamer)

スクリプトを実行すると、Debian, Ubuntu の場合は標準の apt リポジトリから必要な debパッケージ1が自動的にインストールされる。 うまくいくとこんなダイアログが表示される。

f:id:tmtms:20200518021734p:plain

この場合は /dev/video2 がカメラデバイスとなっている。

OK を押すと、端末に

Press enter to end stream

と表示されるので、そのまま放置しておく。Enter を押すと終了する。

これでカメラとして使えるようになってるはず。

なにかテキトーなアプリで動作確認する。

VLC は「メディア」→「キャプチャーデバイスを開く」→「ビデオデバイス名」に /dev/video2 を入力(メニューから選択)して、「再生」を押すと映像が表示される。

Zoom だと設定の「ビデオ」→「カメラ」のメニューに「Dummy video device (0x0000)」というのが増えてた。

スクリプトを終了しても adb が残ってるので、次に起動した時に

Your port 8080 seems to be in use: falling back to Wi-Fi. If you would like to use USB forwarding, please free it up and try again.

みたいな理不尽なことを言われるので、adb は kill しておいた方が良さそう。

いろいろ変更したけどローカルPCに置いておくと無くしそうなので、GitHub で fork して自分用のものを置いておく。

https://github.com/tmtm/ipwebcam-gst/blob/master/prepare-videochat.sh


  1. インストールされるパッケージは、bc, zenity, v4l2loopback-dkms, python-apport, gstreamer1.0-pluseaudio, v4l-utils, gstreamer1.0-tools

sleep infinity で無限に待つ

何もしないで無限に待ち続ける pause(2) みたいなことをするコマンドが無いかなーと探したけど無さそうで、sleep は秒数指定しないといけないのがいまいちなんだけど、まあ sleep 99999999999999 みたいなことすればいいんだけど、それもかっこ悪いなーと思ったら sleep infinity なんて表記があるらしい。

qiita.com

でも sleep のマニュアル見ても infinity については書いてない。

https://www.gnu.org/software/coreutils/manual/html_node/sleep-invocation.html

なんなんだこれ、と思って sleep のソースを見てみた。

https://github.com/coreutils/coreutils/blob/master/src/sleep.c

infinity という単語はどこにも定義されてなくて、アレ?って思ったんだけど、引数を xstrtod って関数で処理してた。 xstrtod は知らなかったんだけど、名前からして strtod の仲間だろうと思って、strtod を見たら

ja.manpages.org

無限 は "INF" または "INFINITY" で表され、大文字小文字は区別されない。

だそうな。なるほどなー。

sleep コマンドの機能じゃなくて、中で使ってる関数の処理だったと。

しかしそれにしてもマニュアルに書いておいて欲しい気はする。

まあ無限と言っても、最終的には nanosleep(2) が呼ばれてて、その引数は time_t で有限なので、実際には無限じゃなくて 百何十年とか何千億年とかなんだろうけども。

Ubuntu で Postfix の SMTP AUTH を設定して fail2ban で認証に失敗したIPアドレスをブロックする

Ubuntu 18.04 の Postfix で次のようにして SMTP AUTH を有効にしました。

/etc/postfix/master.cf

submission inet n       -       n       -       -       smtpd
  -o syslog_name=postfix/submission
  -o smtpd_sasl_auth_enable=yes
  -o smtpd_recipient_restrictions=
  -o smtpd_relay_restrictions=permit_sasl_authenticated,reject

/etc/postfix/main.cf

smtpd_sasl_local_domain = $myhostname
broken_sasl_auth_clients = yes
smtpd_sasl_security_options = noanonymous

/etc/postfix/sasl/smtpd.conf

pwcheck_method: saslauthd
mech_list: PLAIN LOGIN

/etc/default/saslauthd (変更部分のみ)

START=yes

設定反映

# systemctl reload postfix

で、 fail2ban というパッケージをインストールすると、SMTP AUTH に何回か失敗するとそのクライアントの IPアドレスがブロックされるようになります。10分経つとブロックが解除されます。

/etc/fail2ban/jail.d/postfix.conf (作成)

[postfix-sasl]
enabled = true

設定反映

# systemctl reload fail2ban

回数やブロック解除までの時間等は /etc/fail2ban/jail.conf で指定できます。

# "bantime" is the number of seconds that a host is banned.
bantime  = 10m

# A host is banned if it has generated "maxretry" during the last "findtime"
# seconds.
findtime  = 10m

# "maxretry" is the number of failures before a host get banned.
maxretry = 5

ログは /var/log/fail2ban.log に出力されます。こんな感じ。

2019-01-14 23:29:00,987 fail2ban.filter         [1566]: INFO    [postfix-sasl] Found x.x.x.x - 2019-01-14 23:29:00
2019-01-14 23:29:14,921 fail2ban.filter         [1566]: INFO    [postfix-sasl] Found x.x.x.x - 2019-01-14 23:29:14
2019-01-14 23:29:29,230 fail2ban.filter         [1566]: INFO    [postfix-sasl] Found x.x.x.x - 2019-01-14 23:29:29
2019-01-14 23:29:39,035 fail2ban.filter         [1566]: INFO    [postfix-sasl] Found x.x.x.x - 2019-01-14 23:29:39
2019-01-14 23:29:39,189 fail2ban.actions        [1566]: NOTICE  [postfix-sasl] Ban x.x.x.x
2019-01-14 23:39:40,070 fail2ban.actions        [1566]: NOTICE  [postfix-sasl] Unban x.x.x.x

なお fail2ban をインストールするだけで sshd についても有効になります。

/etc/fail2ban/jail.d/defaults-debian.conf

[sshd]
enabled = true

これを false にするかコメントアウトすれば sshd について無効にできます。

ThinkPad でタッチパッドに触れているとボタンイベントを取りこぼす問題

二年くらい前に、トラックパッドに二本以上の指が触れているとかなりの確率でボタンのクリックイベントを取りこぼしてしまうというのを書いたんですが、最近の Linux では簡単に解決できるみたいです。

Twitter で教えてもらいました。

/etc/modprobe.d/psmouse.conf に次のように記述して、

options psmouse synaptics_intertouch=1

再起動するか、modprobe -r psmousemodprobe psmouse すればよいです。

快適!

WSL で Ubuntu デスクトップ環境を作ってみる

富士通クラウドテクノロジーズアドベントカレンダー2018の24日目の記事です。 qiita.com

3ヶ月ほど前から富士通クラウドテクノロジーズ(FJCT)という会社で働いています。 …という話をするとたいてい「何やってる会社?」と聞かれるのですが、「ニフクラというクラウドサービス」というと「ああニフティの」という反応。 「ニフクラは今はニフティじゃないんですよ」というと知らなかったというのがほぼ100%。 ニフクラの「ニフ」はニフティのニフでも日商岩井富士通の略でもなくて、「Navigate Innovative Future」らしいです(自分も最近知った)。

FJCTは、テックブログやってたり、社内LT大会やってたり、月イチでミートアップやってたり、IT勉強会向けに無償で会場提供してたり、「富士通」が名前に入ってる会社にしては面白い会社だと思います。

ただし会社のPCはWindowsしか使えないのが残念。残念なので、WSL を使って Ubuntu デスクトップ環境を試行錯誤しながら作ってみました。個人的な好みによりディストリビューションは Ubuntu MATE です。



WSL Ubuntu をインストール

Microsoft Store から Ubuntu を検索してインストール。

起動すると、作成するユーザー名とパスワードを求められるので入力。

何やら新しいファイルシステム(WslFs)にアップグレードできるみたいなので、コマンドプロンプトからアップグレード。効能不明だけど、アップグレードというからにはきっと何かいいことがあるんでしょう。 初期状態だと数分でおわったけど、ファイルが大量にある場合は何時間たっても終わらなかったのでやらない方がいいかもしれない。

C:\Users\tommy>wslconfig /upgrade Ubuntu
アップグレードしています...
アップグレードが正常に完了しました。

apt upgrade

Ubuntu をアップグレード。1行目はダウンロードサイトを日本のミラーサイトに変更した方が速いんじゃないかと思ってやったけど別にやらなくてもいいかも。

$ sudo sed -i.bak -e 's|http://archive|http://jp.archive|' /etc/apt/sources.list
$ sudo apt update
$ sudo apt upgrade

Ubuntu MATE インストール

Ubuntu MATE のインストール。結構時間がかかる。1時間くらい? dbus を起動しておかないとインストールの途中でエラーになるので起動しておく。

$ sudo service dbus start
$ sudo apt install ubuntu-mate-desktop mate-desktop-environment mate-common mate-core

LightDM 起動

mate-session で起動するかと思ったけど、うまくいかなかったので XDMCP 経由でログインする方法で。

/etc/lightdm/lightdm.conf を次の内容で作成。

[LightDM]
start-default-seat=false

[XDMCPServer]
enabled=true
port=177
$ sudo service lightdm start

なぜか復帰しないけどそのまま放置。

Windows に X Window サーバーをインストールして実行

何でもいいと思いますが、VcXsrv を使いました。 https://sourceforge.net/projects/vcxsrv/

実行

f:id:tmtms:20181223110458p:plain

「One large windows」を選択するのが無難。

f:id:tmtms:20181223110516p:plain

「Open session via XDMCP」を選択

f:id:tmtms:20181223110529p:plain

「Connect to host」に「127.0.0.1」を設定

f:id:tmtms:20181223110541p:plain

f:id:tmtms:20181223110605p:plain

f:id:tmtms:20181223110635p:plain

ログイン画面が出るので最初に登録したパスワードでログイン。

一応それなりに使えます。

日本語化

https://www.ubuntulinux.jp/japanese を参考に:

% wget -q https://www.ubuntulinux.jp/ubuntu-ja-archive-keyring.gpg -O- | sudo apt-key add -
% wget -q https://www.ubuntulinux.jp/ubuntu-jp-ppa-keyring.gpg -O- | sudo apt-key add -
% sudo wget https://www.ubuntulinux.jp/sources.list.d/bionic.list -O /etc/apt/sources.list.d/ubuntu-ja.list
% sudo apt update
% sudo apt upgrade
% sudo apt install ubuntu-defaults-ja

日本語入力として fcitx を使いたいので:

% sudo apt install fcitx fcitx-mozc

Menu → Control Center → Language Support でできるはずなんだけど、エラーが出る。

org.freedesktop.PolicyKit.Error.Failed: ('system-bus-name', {'name': ':1.69'}):
 org.debian.apt.install-or-remove-packages

原因はよくわからないので、あきらめて手動で設定。$HOME/.pam_environment に次の行を設定

LANGUAGE        DEFAULT=ja_JP.UTF-8

一旦ログアウトしてログインして、画面右上のキーボードアイコンを右クリックしてメニューから「設定」→「入力メソッド」で Mozc を追加。

Ctrl+スペースで日本語入力できるようになります。

f:id:tmtms:20181223130054p:plain

再起動

一度実行した WSL は画面を閉じてもそのまま動いてるようなので、完全に再起動するにはコマンドプロンプトから一旦終了させる必要があります。

wslconfig /t Ubuntu

Ubuntu を起動したあとは次の手順を実行します。

$ sudo service cgroupfs-mount start
$ sudo service dbus start
$ sudo service lightdm start

Docker

Docker も動くみたいです。ただし Ubuntu を管理者として実行する必要があります。

$ sudo apt install docker.io
$ sudo service docker start
$ sudo docker run hello-world

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
    (amd64)
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
 https://hub.docker.com/

For more examples and ideas, visit:
 https://docs.docker.com/get-started/

普通に起動した Ubuntu の端末と、管理者として起動した Ubuntu の端末は同じ Ubuntu を共有しているのに、service docker start でちゃんと起動するのは管理者として起動した端末の方だけという。WSL謎い…。

いまいち…

…と、ここまでの設定でまあそれなりに動くようになったんですけど、あくまでもそれなりにって程度です。

Docker が起動しない挙動は謎だし、Compiz をウィンドウマネージャーとして使えないし。

ウィンドウの切り替えキーの Alt-Tab は Windows が奪っちゃうので Ubuntu の中では使えません(これはXサーバー側の問題な気はする)。

日本語化のところでエラーが出たように、管理者として実行する必要がある処理はうまく動きません。 ウィンドウが落ちることもあります。 たぶん、polkit か dbus か systemd まわりだと思うのですが、わかりませんでした。

最近の Linux は systemd が動作しているのが前提になってることがあるので、systemd が動かない WSL では限界かもしれません。

デスクトップ環境として Ubuntu を使う場合は、やはり最新版を使いたいもんですが、18.10 はさらに systemd 依存が進んでいるのか、デスクトップ環境を動かすことができませんでした。

やっぱり本格的にデスクトップ環境として使うには VirtualBox に Ubuntu をインストールして VM のコンソールに直接デスクトップを表示した方がいいです。結局自分もそうしてます。