Hatena::ブログ(Diary)

Okiraku Programming RSSフィード

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

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

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


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


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


またコマンドが入力を受け取る場合、移動先の端末でシェル等がreadしていると、入力が部分的にシェルに吸われてしまって操作が著しく困難になってしまうので、そのようなプロセスは止めておく必要があります。以下のスクリプトではSTOPシグナルを送って眠らせるようにしてあるのですが、screentmuxは、端末の子プロセスが寝たことを検知すると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自分の子プロセスでないプロセスの終了を待機するいい方法がないため

2016-12-31 iPhoneのバッテリー残量を取得

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

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


f:id:NeoCat:20161218073904j:image:w400


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


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


次に考えられるのはUSB越しで取得する方法です*1。最近のLinuxデスクトップ(Fedora 25で確認)にiPhoneUSB接続すると、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%ほど超えたところで充電が止まりました。


f:id:NeoCat:20161231214258p:image:w500


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


f:id:NeoCat:20161231214259p:image:w500


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

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

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

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

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

2016-06-23 マイナンバーカードでSSHしてみた

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

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


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

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



まずは必要なパッケージをインストール。カードリーダーを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 <ファイル名>

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

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

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

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

D

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

発売当初はバックライト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である必要は全くなく、ただのコンパレータでいいのですが、手持ちがなかったので。。)

f:id:NeoCat:20160507052313p:image


上記のように接続して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;
    }
}

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

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を同時実行するとか割と荒っぽいので、大規模な環境で実行しないほうがいいと思います。。が、学習用にはいいかな、と。