片手用Bluetoothキーボードを買ったのでカスタマイズツールを作ってみた

Amazonで、2つのノブの付いた片手用Bluetoothキーボードを買ってみました。Bluetooth付きのもの・USB専用のものや、6/9/12キー他、3ノブのものなど、色々種類があるようです。
いろんなブランド名で出品されており怪しげなのですが、念のためAmazon出荷のものを選んでみました。

片手キーボード (Bluetooth接続 3層レイヤー機能付き ノブ2個付き 9キー) 黒
片手キーボード (Bluetooth接続 3層レイヤー機能付き ノブ2個付き 9キー) 白


接続はUSB Type-Cです。Bluetooth対応版だとリチウムイオン電池を内蔵していて、USB接続中に充電されます(USB接続中はUSBキーボードとして振る舞います)。ノブ付きの片手キーボードデバイスは意外と高価なものが多いので、多少安価な部類に思います(それでもBluetooth版だと価格は上がりますが)。質感もアクリルパネルで悪くはないのですが、裏面がネジ露出でそのままだと置いた場所が傷だらけになるので、ゴム足なりビニールテープなりを貼る等の対策が必須です。
Bluetooth版だと3つのレイヤーに違うキーを割り当ててボタンで切り替えて使うこともできます。

しかし最大の欠点は設定ツールで、商品説明のGoogleDriveリンクで配布されている未署名のWindows用アプリしかなく、ちょっと怪しげな感じで作りも最低限のものです。商品名にはLinux/Macなどと書いてあるのですが、設定するにはWindowsが必須になってしまいます*1


そこでLinux/Mac、あるいはWinでもこのアプリを使わずに設定できるよう、どうやって設定されているのか調べてみました。
やり方は簡単(?)で、Windows(念のため環境が壊れてもいいように仮想環境でやりました)上でこのアプリを動かし、WiresharkでUSBキャプチャして設定時の通信内容からプロトコルを調べます。

*1:一旦設定さえしてしまえばツールがなくてもキー割当はデバイスが記憶してくれるのですが

続きを読む

LinuxのHDDに不良セクタが発生したので修復してみた

NAS(Linux機)からファイルを読み出していたら、突然ハングアップしてしまい‥…dmesgを調べてみるとHDDに不良セクターと思しきI/O Errorが発生していました。

sda: Current: sense key=0x3
    ASC=0x0 ASCQ=0x0
Info fld=0x43c024d6
end_request: I/O error, dev sda, sector 1136665808
Buffer I/O error on device sda, logical block 142083226

確かに、このセクターをddで読み出してみると、延々カシャカシャ音を立てるばかり。しばらくするとI/O Errorで失敗します。

dd if=/dev/sda of=/dev/null skip=$((1136665808/8)) bs=4k count=1
(しばらく固まる)
dd: /dev/sda: Input/output error


こういう時は、このセクターに書き込みを行うと、HDDのファームウェア代替(だいたい)セクターに置き換えてくれるはず。
このセクターはファイルデータの一部であることが分かっているので、とりあえず0で埋めます。(ファイルシステムメタデータの一部だと厄介。と言っても0で埋めてあとでfsckをかけてみるくらいしか無いのですが)

注意点として、セクターは512Byte単位ですが、書き込みはLinuxのページサイズである4KB単位で行わないと、データをいったん読み出してから書き込もうとするので、またI/O Errorになってしまいます。
したがって、以下のようにbs=4kを指定して、セクタ番号を8で除算した値を指定します。間違うと別のデータを破壊してしまうので注意。

sudo dd if=/dev/zero of=/dev/sda seek=$((1136665808/8)) bs=4k count=1
1+0 records in
1+0 records out

書き込みに成功しました。

もう一度読んでみると

dd if=/dev/sda of=/dev/null skip=$((1136665808/8)) bs=4k count=1
1+0 records in
1+0 records out

正常に読み出せるようになっていました(というか、そう見えるように代替セクターに置き換えられていると思われます)。


前後のセクターを読んでみても正常そうなので、しばらくこれで様子を見てみます。

コマンドの入出力が繋がった端末を移動させる

時間のかかるコマンドを起動した後、「あ、tmux(とかscreen)の中で実行すればよかった…」と気づくことがたまにあるわけですが、そんな時に強制的に端末を移動させる方法として、gdbをアタッチし、そのコマンドのファイルディスクリプタ(fd)の示す先を変更してしまうという手があります。


具体的な手順としては、open(2)で移動先の端末を開き、そのfdをdup2(2)の第1引数、移動前の端末に繋がっているfdを第2引数に指定して差し替え、最後にclose(2)で最初にopenした端末を閉じます。


ただし厳密には、対象が端末の場合、各種の状態を持っているので、これをsttyコマンドで合わせる必要があります。そうでないと、対象がviやemacsのような端末制御を行うソフトウェアの場合、正しく操作できなくなります。


またコマンドが入力を受け取る場合、移動先の端末でシェル等がreadしていると、入力が部分的にシェルに吸われてしまって操作が著しく困難になってしまうので、そのようなプロセスは止めておく必要があります。以下のスクリプトではSTOPシグナルを送って眠らせるようにしてあるのですが、screenやtmuxは、端末の子プロセスが寝たことを検知するとCONTシグナルを送ってすぐに起こしてしまうようです。仕方ないので手動でsleepでもさせておくしかなさそうです。。とりあえずスクリプトが実行された端末が移動先になっている場合にはスクリプト内でsleepさせています*1


このほかの工夫として、移動元の親プロセス(通常はシェル)を待機状態から復帰させるために、一旦対象コマンドにSTOPシグナルを送ってサスペンドしたのち、少し待ってCONTシグナルで復帰させています。ついでに、端末のサイズが変わったことをWINCHシグナルでコマンドに通知してあげます。これにより画面の再描画も必要に応じて行われます(あるいは単に無視されます)。


上記の処理をするのが本記事末尾のスクリプトです。

./switch-tty.sh 移動するブロセスID 移動先の端末またはファイル

のように指定して使います。


試してみるには、Linux上(またはssh)で端末を2つ開き、一方で何かのコマンド (例えば vi ) を起動し、もう一方で (必要ならtmuxなどを実行したのち)

./switch-tty.sh `pidof vi` `tty`

などと実行すると、

Current stdin: /dev/pts/5
Target fds: 0 1 2
session leader: 23965
Switching to this TTY
Continue? (y/N) 

と確認が出ますので、yを入力します。するとおもむろにgdbがごにょごにょ動いたのち、スクリプトを実行した端末にviが移動してきます。あとは、移動元の端末で disown を実行すれば、閉じてしまうことができます。


(なお、移動先でviを終了してもすぐにはシェルは帰ってきません。これはコマンドの終了を1分に1回しかチェックしていないためです*2。対象のコマンドの挙動次第ですが、 ctrl-C を入力すればsleepが中断されてすぐにシェルが戻ってくるかもしれません。)


以下、スクリプト
https://gist.github.com/NeoCat/f662cfd71c65eed7b59baed14eb3400c

*1:元々いたプロセスを終了してしまうわけにはいきません。セッションリーダーが閉じると端末そのものが閉じられてしまうからです

*2:自分の子プロセスでないプロセスの終了を待機するいい方法がないため

iPhoneのバッテリー残量を取得

iPhoneのバッテリーがかなりヘタってちょっと使うとすぐに電源が落ちてしまうようになってしまったので、自分で交換してみました。やり方を紹介したページを見つつ、バッテリー\2000 + 工具セット \1000 で無事交換成功し、日中そこそこ使っても丸一日持つようになりました。



DIGIFORCE 交換用PSEバッテリー iPhone6用 1810mAh LPB-DIGI6

DIGIFORCE 交換用PSEバッテリー iPhone6用 1810mAh LPB-DIGI6


さて、バッテリーの寿命を良くする方法として、80〜90%ほど充電されたら繋ぎっぱなしにせずに充電をやめると良いという説があるようです。真偽のほどは定かではありませんし、大きな効果があるならそういった機能が実装されているでしょうから、おそらくいちいち気にするほどの効果はないのでしょう。とはいえ、意識しなくとも自動的に充電が止まってくれるのならそうしてみても良いかな?という気になったので、やり方を考えてみました。


まず、iPhoneのバッテリー残量を取得する方法ですが、なんらかのアプリを使えないでしょうか? しかしiOSの場合、アプリがバックグラウンドで定期的に処理をするのは、アクセサリへのアクセスや、音楽再生や位置情報を使用するアプリに限られており、電池監視のためにはトリッキーなことをする必要があるので候補から外しました。


次に考えられるのはUSB越しで取得する方法です*1。最近のLinuxデスクトップ(Fedora 25で確認)にiPhoneをUSB接続すると、upowerコマンドでiPhoneのバッテリー値を取得できます。下記はiPadの場合ですが、まず一覧で名前を確認し、

$ upower -e
/org/freedesktop/UPower/devices/computer_3_1
/org/freedesktop/UPower/devices/mouse_0003o046Do1024x000A
/org/freedesktop/UPower/devices/keyboard_0003o046Do2011x000B
/org/freedesktop/UPower/devices/DisplayDevice

それっぽいデバイスを指定すると、

$ upower -i /org/freedesktop/UPower/devices/computer_3_1
  native-path:          /sys/devices/pci0000:00/0000:00:14.0/usb3/3-1
  vendor:               Apple_Inc.
  model:                iPad
  serial:               *****************************************
  power supply:         no
  updated:              20161231221652(14 seconds ago)
  has history:          yes
  has statistics:       no
  computer
    warning-level:       none
    percentage:          100%
    icon-name:          'battery-full-charged-symbolic'

という感じで情報が出力されるので、このpercentageを見るというのが一つの方法です。GUIで設定(gnome-control-center)→電源 で残量を確認できるのもこの情報を表示しています。


これを見て、80%を超えたらUSBの電源をOFFにする方法が取れるでしょう。LinuxからのUSBの電源制御には、最近だと以下のハブが使えるそうです。
LinuxからUSB HUBの電源のON/OFFを制御してみる - memoメモ


ただ欠点として、USBから切断してしまうとそれ以降の情報は取れないので、充電完了後に使用していると電池が減っていってしまうということになります*2


別のやり方として、iTunesをインストール済みのMacまたはWindowsであれば、libimobiledeviceを導入することで、Wi-Fi経由でも電池残量などの情報が取得できます。たまたまWiFiで電源をON/OFFできるコンセントを作ってあったので、全てWiFi越しでやれるということもあり、今回はこの方法を試してみました。


まず、libimobiledeviceを導入します。Macであればbrewで一発です。とはいえiOS10のせいか、HEADでないとうまく動作しませんでした。

$ brew install --HEAD libimobiledevice

これで、
idevice_id -l コマンドでUSBまたはWiFiで繋がっているiOSバイスのシリアルを調べておき、ideviceinfoでバッテリー情報を取得します。

$ ideviceinfo -u ****************(idevice_idで表示されたシリアル) -q com.apple.mobile.battery
BatteryCurrentCapacity: 100
BatteryIsCharging: false
ExternalChargeCapable: true
ExternalConnected: true
FullyCharged: false
GasGaugeCapability: true
HasBattery: true

BatteryCurrentCapacityが%単位でのバッテリー残量です。
rubyであれば

$IDEVICEINFO_CMD = '/usr/local/bin/ideviceinfo'

def get_batt(device_id)
  result = `#{$IDEVICEINFO_CMD} -u #{device_id} -q com.apple.mobile.battery`
  if result =~ /BatteryCurrentCapacity:\s*(\d+)/
    return $1.to_i
  end
  nil
end

というような関数でバッテリー残量を取得できるようになります。


ただし,iPhoneがスリープ状態になっている場合、WiFiには90〜300秒に一度、数秒間しか接続されないようで、この瞬間しか情報がとれません。とりあえず、2秒に一度くらいポーリングをかけることにします*3。また、それでもかなり長いことWiFiに接続してこないこともあるようなので、そのような場合には一度充電をOFF→ON(またはON→OFF)することで、iPhoneをウェイクアップさせれば情報がとれます*4


この結果を見て、80%以上で充電停止、80%未満で再開するようにします。また、充電中にMacがスリープしてしまうと充電されっぱなしになるので、充電中はcaffeinateコマンドでスリープを抑制しています。全体的にはこんなスクリプトになりました。


30%充電の状態からこのスクリプトを動かしながら充電を試して見た結果、こんな感じになりました。運悪く80%になるわずか手前でWiFi接続が切れたらしく、80%を3%ほど超えたところで充電が止まりました。



そのままiPhoneを使っていると、79〜80%で維持されるように充電がON/OFFされます。



でもこれ結局充放電してるわけで、バッテリーに優しい気はしない。。まあ、あくまで実験ということで。

*1:Bluetooth LEのBattery Serviceを使ってとれないか試したのですが、いざ取得しようとすると認証を要求されてしまい失敗しました。

*2:定期的に再接続して充電残量をチェックすればいいかも

*3:本当はlibimobiledeviceから接続イベントを取れれば良いのですが、ポーリングでも大した負荷ではないのて今回は適当に済ませてしまいました。

*4:この時、いちいち接続音やバイブレータが鳴りますが…

マイナンバーカードでSSHしてみた

マイナンバーカードでSSHする - AAA Blog


という記事が出ていたので、マイナンバーカードを使ったSSHログインをLinux上でやってみました。環境は出たばっかりのFedora 24です。

カードリーダーは、上記の記事のような非接触式ではなく、接触式のICカードリーダー NTTCom SCR3310を利用しました。


SCM ICカードリーダー/ライター B-CAS・住基カード対応 SCR3310/v2.0 【簡易パッケージ品】

SCM ICカードリーダー/ライター B-CAS・住基カード対応 SCR3310/v2.0 【簡易パッケージ品】


まずは必要なパッケージをインストール。カードリーダーをUSB接続し、スマートカードデーモンpcscdを起動します。

$ sudo dnf install openssl-devel readline-devel zlib-devel pcsc-lite pcsc-lite-devel pcsc-lite-ccid pcsc-tools
$ sudo systemctl start pcscd


開発中の個人番号カード対応版OpenSCをcloneしてきて、ビルド、インストールします。

$ git clone https://github.com/jpki/OpenSC.git
$ cd OpenSC
$ ./bootstrap
$ ./configure
$ make
$ sudo make install

ではカードをセットして、公開鍵を読み出しましょう。カードの裏表を間違えないように注意(ICチップの端子のある面が上)。間違ってると読めないというエラーになります。。

$ pkcs15-tool --read-ssh-key 1
Using reader with a card: NTT Communications Corp. SCR3310-NTTCom USB SmartCard Reader [Vendor Interface] 00 00
ssh-rsa *******...... User Authentication Certificate
zsh: bus error  sudo =pkcs15-tool --read-ssh-key 1

ってなんかバスエラーを起こしました。がとりあえず読めたから気にしない(何


これをコピペしてSSHサーバの ~/.ssh/authorized_keys に追加します。同一マシン(ローカルホスト)でもいいでしょう。


そうしておいて、ビルドしたOpenSCのPKCSモジュールを指定してSSHログインしてみると...

$ ssh 192.168.xx.yy -I /usr/local/lib/opensc-pkcs11.so
Enter PIN for 'JPKI (User Authentication PIN)': **** ← 公的個人認証用の4桁のPIN番号
Last login: Fri Jun 24 00:37:23 2016 from 192.168.xx.zz
$ 

無事ログインできました。


なおPIN番号は3回間違えると閉塞されてしまい役所で手続きが必要になるのでご注意を。。
(3種類ある4桁の暗証番号のうちの、利用者証明用電子証明書の(マイナポータルへのログイン、コンビニでの公的な証明書の交付などで使う)PIN番号です。)


他に

$ pkcs15-tool -c 

で認証用証明書のシリアル番号などを確認したり、

$ pkcs15-tool -r 1 

で認証用証明書の情報を読み出し、ファイルにコピペして(リダイレクトするとSEGVするせいで書き出されない…)

$ openssl x509 -text -noout -in <ファイル名>

などとすると有効期限や発行者の情報(自治体とか)が確認できます。認証用証明書の方にはマイナンバーはもちろん、住所や氏名などは入っていないことが確認できます。

近接センサでRaspberryPi LCDのバックライトを点灯

Raspberry Pi 2に7インチタッチディスプレイをつけて、パッと見たい情報(天気予報とか時計とかバス時刻表とか)を表示するのに使っているのですが、常時つけっぱなしになっているのが気になっていました。周囲に人がいるときだけオンになったらいいなと思い、近接センサで一定範囲内(だいたい1.5m以内)に反応があった場合にバックライト点灯→しばらく反応がないと消灯するようにしました。


バックライトの制御コマンド

発売当初はバックライトON/OFFが制御できませんでしたが、最近のカーネルなら/sys/class/backlight/rpi_backlight/bl_power に0/1をwriteすることで ON/OFFができるようになっています(輝度変更は効きませんが)。また、XのDPMS (Energy Star)とも連動してON/OFFされます。今回は一定時間で消灯したいのと、タッチしたらONになって欲しいので、DPMSを使います。

Xが起動している状態で、

$ xset dpms force on
$ xset dpms force off

でバックライトのON/OFFが可能です。また、

$ xset dpms 120 120 120

とすると120秒間操作しないと自動でOFFになります。現在の状態確認はxset qを使います。

$ xset q
...
DPMS (Energy Star):
  Standby: 120    Suspend: 120    Off: 120
  DPMS is Enabled
  Monitor is Off

近接センサの接続

近接時の検出は以下の測距センサを使いました。距離に応じた電圧がアナログ出力されるものです。
シャープ測距モジュール GP2Y0A02YK: センサ一般 秋月電子通商 電子部品 ネット通販

Raspberry Piには直接アナログ入力することができないため、一旦Arduino Pro Miniを通して一定距離内に入った時にデジタルで信号を出力するようにしています。動画のRasPiの後ろで近接時にLEDが点灯しているのがArduino Pro Miniです。(別にデジタル変換はArduinoである必要は全くなく、ただのコンパレータでいいのですが、手持ちがなかったので。。)


上記のように接続してGPIOの17を読めば、近接検出ができます。GPIOへのアクセスは

# echo 17 > /sys/class/gpio/export

を実行しておき、

# cat /sys/class/gpio/gpio17/value

のようにすれば0または1として値を読み出すことができます。

近接センサでバックライト制御

以下のスクリプトでGPIOを定期的に監視し、近接時にxsetでバックライトの点灯させます。なお、点灯中にforce onしても無操作タイマーが更新されず、すぐに自動消灯してしまったりするので、近接中は定期的に時間を再設定することでタイマーをリスタートしておきます。

#!/usr/bin/perl
use Time::HiRes "sleep";
open my $in, "/sys/class/gpio/gpio17/value";
while (true) {
    seek $in, 0, 0;
    my $val = <$in>;
    if ($val == 1) {
        `xset dpms force on`;
        `xset dpms 120 120 120`;
        print "ON\n";
        sleep 60;
    } else {
        sleep 0.1;
    }
}

Open vSwitchやNamespaceの関係図をコマンド一つで描く

LinuxでSDNっぽいことをOpen vSwitchでやろうとすると、BridgeやらNamespaceやらInterfaceやらが増えまくって全貌の把握が困難になってきます。また、なぜかpingが通らないときなど、あっちこっちでtcpdumpして回るのは大変です。

そこで、コマンド一つでOpen vSwitchやNamespaceの関係図を描き、ついでに全interfaceをtcpdumpしてpingがどこを通っているかも色で示すツールを作りました。


今のところ単一ホスト内のみなので複数nodeあるときは各nodeで実行する必要がありますが。

使い方

ソースは https://github.com/NeoCat/ovsimager にあります。sudo できるユーザかrootで

 $ sudo gem install ovsimager
 $ sudo yum install graphviz
 $ ovsimager

とインストールして実行すると、interfaces.png というファイルに関係図が出力されます。


以下はOpenStack Neutron DVR(all-in-one構成)でinstanceにFloating IPを振った状態です。
Open vSwitch Virtual SwitchとLinuxBridge内のInterface、およびNamespaceとの関係(routingに使われうる、IPが振られたインターフェイスのみ)が点線で描かれます。vethやOpen vSwitch間のpeerは実線で繋がります。


pingをトレースするときは、400byteのpayloadのpingを打ちながら、

 $ sudo ovsimager -d

とします。すると、全インターフェイスtcpdumpが数秒間起動され、400byteのping (vxlanも含む) が通った場所を記録、図にpingの宛先・送信元IPアドレスが追加され、色もつきます。(黄: ICMP Echo Request/Response両方が通過、ピンク: Requestのみ通過、赤:Responseのみ通過)


なお ovsimager 自身に ping を実行させることもできます。

 $ sudo ovsimager -d -f 10.0.0.1 -t 192.0.2.1

pingのトレース時は全interfaceでtcpdumpを同時実行するとか割と荒っぽいので、大規模な環境で実行しないほうがいいと思います。。が、学習用にはいいかな、と。