PQI Air Cardをタニタの体重計で動かそうとして失敗し悲しみに暮れているところ。

ふと、ダイナミックDNSサービスの設定を変更する自作Pythonスクリプトの存在を思い出した。
ルータのWeb画面に表示されるグローバルIPアドレスの変更を検知する簡単なもの。

今も実家の古いPCで動いているがいつ壊れるか分からない。
その程度の処理であればPQI Air Cardの組み込みLinuxでも十分可能だろうと考えたのでやってみた。

いざ動作させようと思ってはみたものの、Pythonスクリプトの動作環境が無ければ話が始まらない。
PQI Air Cardは純正でCGI動作用のPerlが入っているが、Pythonは入っていない模様。
有志によりRubyはビルドされているようだが、Pythonは存在しないので自前でビルドせざるを得ない。

以下は手元のLinux PC (Ubuntu)の作業メモ

PQI Air Cardの環境を確認する

  • CPU

    
    # cat /proc/cpuinfo
    Processor       : ARM926EJ-S rev 5 (v5l)
    BogoMIPS        : 25.24
    Features        : swp half fastmult edsp java
    CPU implementer : 0x41
    CPU architecture: 5TEJ
    CPU variant     : 0x0
    CPU part        : 0x926
    CPU revision    : 5
    
    Hardware        : KeyASIC Ka2000 EVM
    Revision        : 0000
    Serial          : 0000000000000000
    

    arm926ej-s(arm v5l)らしい

  • 起動パラメータ

    
    # cat /proc/cmdline
    root=/dev/ram0 rw console=ttyS0,38400n8 mem=32M initrd=0x500000,3M
    
  • メモリ

    32MBなので大事に使おう。

    
    # cat /proc/meminfo
    MemTotal:          29652 kB
    MemFree:           11196 kB
    Buffers:             732 kB
    Cached:            13896 kB
    

クロスコンパイル環境の構築

  • ARM向けGNU Toolchainを入手する

    CodeSourceryのARM向けSourcery CodeBench Liteを選択するが、今は公開を停止している。
    検索すれば見つかりそうだけど・・・

    Sourcery CodeBench Lite releases for ARM EABI, ARM GNU/Linux, ARM AARCH64-Linux, IA32/IA64 GNU/Linux and ELF are no longer available.

    LauchpadのToolchainはsys/dirent.hがnot supportedでエラーになる。

  • 展開してパスを通す

    クロスコンパイル環境を使えるようにする。

    $ tar xf arm-2014.05-29-arm-none-linux-gnueabi-i686-pc-linux-gnu.tar.bz2
    $ mv arm-2014.05-29-arm-none-linux-gnueabi-i686-pc-linux-gnu ~/arm-dev-sc
    $ ln -s ~/arm-dev{-sc,}
    $ echo 'export PATH=$PATH:~/arm-dev/bin' >> ~/.bashrc
    $ . ~/.bashrc

    パスが通ると、補完が効くようになっているはず。

    $ arm-<TAB>
    arm-none-linux-gnueabi-addr2line   arm-none-linux-gnueabi-gcc-ranlib
    arm-none-linux-gnueabi-ar          arm-none-linux-gnueabi-gcov
    arm-none-linux-gnueabi-as          arm-none-linux-gnueabi-gdb
    arm-none-linux-gnueabi-c++         arm-none-linux-gnueabi-gprof
    arm-none-linux-gnueabi-c++filt     arm-none-linux-gnueabi-ld
    arm-none-linux-gnueabi-cpp         arm-none-linux-gnueabi-nm
    arm-none-linux-gnueabi-cs          arm-none-linux-gnueabi-objcopy
    arm-none-linux-gnueabi-cs-daemon   arm-none-linux-gnueabi-objdump
    arm-none-linux-gnueabi-elfedit     arm-none-linux-gnueabi-ranlib
    arm-none-linux-gnueabi-g++         arm-none-linux-gnueabi-readelf
    arm-none-linux-gnueabi-gcc         arm-none-linux-gnueabi-size
    arm-none-linux-gnueabi-gcc-4.8.3   arm-none-linux-gnueabi-strings
    arm-none-linux-gnueabi-gcc-ar      arm-none-linux-gnueabi-strip
    arm-none-linux-gnueabi-gcc-nm
    

ビルド

  • Pythonソースコードとパッチの準備

    クロスコンパイル環境を整えたPCにPythonソースコードとクロスコンパイル用パッチをダウンロードする。
    動かしたいスクリプトはPython2向けに書いたので、Pythonのバージョンはパッチに合わせて2.7.3にする。

    $ wget http://www.python.org/ftp/python/2.7.3/Python-2.7.3.tar.bz2
    $ wget http://randomsplat.com/wp-content/uploads/2012/10/Python-2.7.3-xcompile.patch
    
  • ホスト用Pythonの準備

    クロスコンパイルで使うPython(buildpython,buildpgen)をビルドする。

    $ tar xf Python-2.7.3.tar.bz2 && cd Python-2.7.3
    $ ./configure
    $ make python Parser/pgen
    $ mv python buildpython
    $ mv Parser/pgen buildpgen
    $ make distclean
    
  • クロスコンパイル用パッチを適用

    $ patch -p1 < ../Python-2.7.3-xcompile.patch
    patching file configure
    patching file Makefile.pre.in
    patching file setup.py
    
  • ARM向けにconfigure

    $ CC=arm-none-linux-gnueabi-gcc CXX=arm-none-linux-gnueabi-g++ AR=arm-none-linux-gnueabi-ar RANLIB=arm-none-linux-gnueabi-ranlib BASECFLAGS=-mcpu=arm926ej-s \
    ./configure --host=arm-none-linux-gnueabi --build=x86_64-linux-gnu --prefix=/usr
    
  • ARM向けにmake

    $ make HOSTPYTHON=./buildpython HOSTPGEN=./buildpgen CROSS_COMPILE=arm-none-linux-gnueabi- CROSS_COMPILE_TARGET=yes HOSTARCH=arm-none-linux-gnueabi BUILDARCH=x86_64-linux-gnu
    

    一部のモジュールは外部のライブラリとリンクできないので使えないらしい。

    Python build finished, but the necessary bits to build these modules were not found:
    _bsddb             _curses            _curses_panel
    _sqlite3           _ssl               _tkinter
    bsddb185           bz2                dbm
    dl                 gdbm               imageop
    nis                readline           sunaudiodev
    zlib
    To find the necessary bits, look in setup.py in detect_modules() for the module's name.

    圧縮アルゴリズムや簡易データベース接続など、機能の一部を外部に求めるモジュールは使用できない。

    ビルドされたpythonバイナリにreadelfしてみた。

    $ arm-none-linux-gnueabi-readelf -a ./python | grep 'Shared library'
     0x00000001 (NEEDED)                     Shared library: [libpthread.so.0]
     0x00000001 (NEEDED)                     Shared library: [libdl.so.2]
     0x00000001 (NEEDED)                     Shared library: [libutil.so.1]
     0x00000001 (NEEDED)                     Shared library: [libm.so.6]
     0x00000001 (NEEDED)                     Shared library: [libgcc_s.so.1]
     0x00000001 (NEEDED)                     Shared library: [libc.so.6]
    

    リンクされているのはPQI Air Cardにも存在するライブラリ類のみだった。
    実行時のメモリ節減のためスタティックリンクはやめておいた。

  • rootfsに転送するファイルをまとめる

    $ make install HOSTPYTHON=./buildpython CROSS_COMPILE=arm-none-linux-gnueabi- CROSS_COMPILE_TARGET=yes prefix=`pwd`/_install
    

    stripして縮める

    $ cp -rf _install{,_strip}
    $ ls -1 _install_strip/lib/python2.7/lib-dynload/*.so | xargs -n1 arm-none-linux-gnueabi-strip
    

    FATのファイルシステムではsymlinkを使えないので、実体を使うように変更するとか細かい部分は適宜調整する。

PQI Air Cardに転送

  • 追加コマンド類の整備

    せっかくなので、@tonsuke85氏、@osakanataro氏の成果物を利用する。
    Flucard向けに纏められているdeploy_20120420.zipが、PQI Air Cardでも利用可能なので導入する。

    autorun.shを自分向けに改変する。

    #!/bin/sh
    ln -s /mnt/sd/DCIM/122_TREK/bin/* /bin/
    ln -s /mnt/sd/DCIM/122_TREK/usr/bin/* /usr/bin/
    ln -s /mnt/sd/DCIM/122_TREK/usr/local /usr/local
    ln -s /mnt/sd/DCIM/122_TREK/usr/lib /usr/lib
    ln -s /mnt/sd/DCIM/122_TREK/usr/include /usr/include
    ln -s /mnt/sd/DCIM/122_TREK/usr/libexec /usr/libexec
    ln -s /mnt/sd/DCIM/122_TREK/sbin/* /sbin/
    rm /lib/libpthread.so.0
    ln -s /mnt/sd/DCIM/122_TREK/lib/* /lib/
    ln -s /mnt/sd/DCIM/122_TREK/etc/* /etc/
    ln -s /mnt/sd/DCIM/122_TREK/www/cgi-bin/connect2hotspot.cgi /www/cgi-bin/
    rm /bin/vi
    ln -s /mnt/sd/DCIM/122_TREK/busybox /bin/vi
    ln -s /mnt/sd/DCIM/122_TREK/busybox /bin/top
    ln -s /mnt/sd/DCIM/122_TREK/busybox /bin/awk
    ln -s /mnt/sd/DCIM/122_TREK/busybox /bin/dd
    rm /usr/bin/hexdump
    ln -s /mnt/sd/DCIM/122_TREK/busybox /usr/bin/hexdump
    ln -s /mnt/sd/DCIM/122_TREK/busybox /bin/killall
    ln -s /mnt/sd/DCIM/122_TREK/busybox /bin/less
    ln -s /mnt/sd/DCIM/122_TREK/busybox /bin/passwd
    ln -s /mnt/sd/DCIM/122_TREK/busybox /bin/sed
    ln -s /mnt/sd/DCIM/122_TREK/busybox /bin/tar
    rm /usr/bin/telnet
    ln -s /mnt/sd/DCIM/122_TREK/busybox /usr/bin/telnet
    ln -s /mnt/sd/DCIM/122_TREK/busybox /bin/whoami
    if [ -e /mnt/sd/enable_sshd ]; then
    if [ -s /mnt/sd/enable_sshd ]; then
    dropbear -A -N root -C '-' -U 0 -G 0 -R /mnt/sd/DCIM/122_TREK/authorized_keys -s
    else
    dropbear -A -N root -C password -U 0 -G 0
    fi
    fi
    if [ -e /mnt/sd/disable_telnetd ]; then
    killall telnetd
    fi
    if [ -e /mnt/sd/enable_ftpd ]; then
    killall tcpsvd
    tcpsvd 0 21 ftpd -w / &
    fi
    if [ -e /mnt/sd/enable_cron ]; then
    ln -s /mnt/sd/DCIM/122_TREK/busybox /usr/sbin/crond
    ln -s /mnt/sd/DCIM/122_TREK/busybox /usr/bin/crontab
    touch /etc/crontab
    mkdir -p /var/spool/cron/crontabs
    touch /var/spool/cron/crontabs/root
    /usr/sbin/crond -L /dev/null
    fi
    if [ -e /mnt/sd/enable_wifista ]; then
    sleep 5
    /usr/bin/w2 &
    fi
    

    /mnt/sd以下に特定の名前のファイルを設置すると、動作が変わるようにした。

    • /mnt/sd/enable_sshdファイルがあればsshdを起動する
      • /mnt/sd/enable_sshdファイルのファイルサイズが1以上なら公開鍵認証モード
        (鍵は/mnt/sd/DCIM/122_TREK/authorized_keys)
      • /mnt/sd/enable_sshdファイルのファイルサイズが0ならパスワード認証モード
        (パスワードはpassword)
    • /mnt/sd/disable_telnetdファイルがあればtelnetdを止める
    • /mnt/sd/enable_ftpdファイルがあればルートディレクトリにアクセスできるftpdを起動する
    • /mnt/sd/enable_cronファイルがあればcrondとcrontabを有効にして、crondを起動する
    • /mnt/sd/enable_wifistaファイルがあればWi-FiをSTAモード(クライアントモード)で起動する
  • ビルドしたPythonを導入

    _install_strip以下のbin/とlib/を上記で展開したDCIM/122_TREK/usr以下にコピーする。

    DCIMディレクトリごとPQI Air CardのSDカードに転送する。

動作確認


# /mnt/sd/DCIM/122_TREK/busybox uname -a
Linux (none) 2.6.32.28 #4 PREEMPT Thu Jul 11 15:51:47 JST 2013 armv5tejl GNU/Linux
# python
Python 2.7.3 (default, Mar  1 2015, 19:57:36)
[GCC 4.8.3 20140320 (prerelease)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import platform
>>> platform.uname()
('Linux', '(none)', '2.6.32.28', '#4 PREEMPT Thu Jul 11 15:51:47 JST 2013', 'armv5tejl', '')
>>> platform.version()
'#4 PREEMPT Thu Jul 11 15:51:47 JST 2013'
>>> platform.machine()
'armv5tejl'
>>>

件のスクリプトはcronで動かす。


*/5 * * * * root /mnt/sd/ddnsup/ddns_update.sh

参考文献

おわり