into the void

ソフトウェアに関する雑多な調査日記

Androidを載せたbeagleboard-xmをTimeMachine用のNASにするまで(其の弐)

avahiのポーティング

avahi-daemonbeagleboard-xm上のAnroidで動作するようにポーティングする。
toolchainはberkeley DBのビルドの時と同じようにcodesourceryからもってきたarm-none-linux-gnueabiのコンパイラを使う。ライブラリ群はandroid上のものと異なるのですべてstatic linkする。
https://sourcery.mentor.com/sgpp/lite/arm/portal/release324

依存ライブラリの準備

avahiをビルドするために必要なexpatとlibdaemonをビルドする。

expat

XML処理用のライブラリ。expat。
これはAndroidをビルドしたときに使ったrowboatソースツリーの中に入っていたのでそれを使うことにした。ただ、通常は共有ライブラリとしてビルドされているので、staticなライブラリとしてビルドしなおした。
external/expat/Android.mkの末尾を$(BUILD_SHARED_LIBRARY)から$(BUILD_STATIC_LIBRARY)に変更してmm。out/target/product/beagleboard/obj/STATIC_LIBRARIES/libexpat_intermediatesにlibexpat.aができるので、これを適当なフォルダにコピーしておく。今回は~/lib.myFroyo/libexpat.aとした。

libdaemon

デーモンプログラムを作るときにユーティリティ的に使えるライブラリ。これはAndroidのソースツリーに入っていないのでサイトからダウンロードしてきてビルドする。バージョンは0.14を使った。
ロスコンパイル用のオプション付きでconfigureをしてmakeするわけだが、そのままのconfigureでは実行時にエラーする。TARGET環境にはないコマンド(setpgrp)を使ったチェックロジックを実行しようとする部分が問題なので、その箇所をコメントアウトした。

$ diff configure configure.org 
13786a13787,13860
> { $as_echo "$as_me:$LINENO: checking whether setpgrp takes no argument" >&5
> $as_echo_n "checking whether setpgrp takes no argument... " >&6; }
> if test "${ac_cv_func_setpgrp_void+set}" = set; then
>   $as_echo_n "(cached) " >&6
> else
>   if test "$cross_compiling" = yes; then
>   { { $as_echo "$as_me:$LINENO: error: cannot check setpgrp when cross compiling" >&5
> $as_echo "$as_me: error: cannot check setpgrp when cross compiling" >&2;}
>    { (exit 1); exit 1; }; }
> else
>   cat >conftest.$ac_ext <<_ACEOF
> /* confdefs.h.  */
> _ACEOF
> cat confdefs.h >>conftest.$ac_ext
> cat >>conftest.$ac_ext <<_ACEOF
> /* end confdefs.h.  */
> $ac_includes_default
> int
> main ()
> {
> /* If this system has a BSD-style setpgrp which takes arguments,
>   setpgrp(1, 1) will fail with ESRCH and return -1, in that case
>   exit successfully. */
>   return setpgrp (1,1) != -1;
>   ;
>   return 0;
> }
> _ACEOF
> rm -f conftest$ac_exeext
> if { (ac_try="$ac_link"
> case "(($ac_try" in
>   *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
>   *) ac_try_echo=$ac_try;;
> esac
> eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
> $as_echo "$ac_try_echo") >&5
>   (eval "$ac_link") 2>&5
>   ac_status=$?
>   $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
>   (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
>   { (case "(($ac_try" in
>   *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
>   *) ac_try_echo=$ac_try;;
> esac
> eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
> $as_echo "$ac_try_echo") >&5
>   (eval "$ac_try") 2>&5
>   ac_status=$?
>   $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
>   (exit $ac_status); }; }; then
>   ac_cv_func_setpgrp_void=no
> else
>   $as_echo "$as_me: program exited with status $ac_status" >&5
> $as_echo "$as_me: failed program was:" >&5
> sed 's/^/| /' conftest.$ac_ext >&5
> 
> ( exit $ac_status )
> ac_cv_func_setpgrp_void=yes
> fi
> rm -rf conftest.dSYM
> rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
> fi
> 
> 
> fi
> { $as_echo "$as_me:$LINENO: result: $ac_cv_func_setpgrp_void" >&5
> $as_echo "$ac_cv_func_setpgrp_void" >&6; }
> if test $ac_cv_func_setpgrp_void = yes; then
> 
> cat >>confdefs.h <<\_ACEOF
> #define SETPGRP_VOID 1
> _ACEOF
> 
> fi
15430,15431c15504
< ac_cr='
< '
---
' ac_cr='

configureのオプションは下記のとおり。

./configure --host=arm-none-linux-gnueabi CC=arm-none-linux-gnueabi-gcc CFLAGS="--sysroot=/android/toolchain/arm/arm-none-linux-gnueabi/libc/" LDFLAGS="--sysroot=/android/toolchain/arm/arm-none-linux-gnueabi/libc/ -static"

makeを実行してできあがる.libs/libdaemon.aを適当なフォルダにコピーしておく。今回はlib.mybuild/libdaemon.aとした。

avahiのconfigure

ロスコンパイル用にオプション設定してconfigureを実行する。
ポイントは下記。

  • static linkされるようにLDFLAGSに"-Wl,-Bstatic"と"-static"。ふつうは"-static"だけでいいはずだがなぜかリンカのオプションに反映されなかったので"-Wl"で直接"-Bstatic"をリンカに渡すようにした。
  • libgccに依存しないようにLDFLAGSに"-static-libgcc"。
  • ビルドの対象を必要最低限にするように--disable-XXXで不要な機能を無効化。dbusもとりえあず無効にした。(dbusはrowboatのソースツリーに入っているのでもし必要であれば有効にできるはず)
  • avahiの実行ユーザ、グループをrootにするように--with-avahi-userと--with-avahi-grouで設定。これをしないとavahiという名前のユーザ、グループが必要になる。android上ではユーザ追加が簡単にできない(というかやり方をしらない。/etc/passwd方式じゃない)のでrootにしておく。
  • インストール先のディレクトリをprefixで/data/avahiに指定。このディレクトリ配下で設定ファイル等を探しにいくことになるためデフォルト(/usr/local)から変更しておく。
./configure --host=arm-none-linux-gnueabi CC=arm-none-linux-gnueabi-gcc CFLAGS="-I/android/include.myFroyo/expat -I/android/include.myFroyo/dbus -I/android/libdaemon/libdaemon-0.14 --sysroot=/android/toolchain/arm/arm-none-linux-gnueabi/libc" LDFLAGS="-Wl,-Bstatic -static -static-libgcc -lpthread -L/android/lib.myFroyo -L/android/libdaemon/libdaemon-0.14/libdaemon/.libs --sysroot=/android/toolchain/arm/arm-none-linux-gnueabi/libc" --with-distro=none --disable-qt3 --disable-qt4 --disable-gtk --disable-gtk3 --disable-gdbm --disable-python --disable-pygtk --disable-python-dbus --disable-mono --disable-monodoc --disable-doxygen-doc --disable-doxygen-dot --disable-manpages --disable-dbus --prefix=/data/avahi --with-avahi-user=root --with-avahi-group=root
ソースの修正

avahi-daemon/main.cで実行ユーザ、グループの情報をgetpwnam()、getprnam()でとってきて使おうとするがandroid上ではうまくいかない(/etc/passwdを使わない独自形式のユーザ管理のためか?)ので、この処理に依存する部分をコメントアウトした。

static int make_runtime_dir(void) {
    int r = -1;
    mode_t u;
    int reset_umask = 0;
    struct passwd *pw;
    struct group * gr;
    struct stat st;
    
#if 0
    if (!(pw = getpwnam(AVAHI_USER))) {
        avahi_log_error( "Failed to find user  '"AVAHI_USER"'.");
        goto fail;
    }

    if (!(gr = getgrnam(AVAHI_GROUP))) {
        avahi_log_error( "Failed to find group '"AVAHI_GROUP"'.");
        goto fail;
    }
#endif
    
    u = umask(0000);
    reset_umask = 1;

    if (mkdir(AVAHI_DAEMON_RUNTIME_DIR, 0755) < 0 && errno != EEXIST) {
        avahi_log_error("mkdir(\""AVAHI_DAEMON_RUNTIME_DIR"\"): %s", strerror(errno));
        goto fail;
    }

#if 0   
    chown(AVAHI_DAEMON_RUNTIME_DIR, pw->pw_uid, gr->gr_gid);
#endif
    if (stat(AVAHI_DAEMON_RUNTIME_DIR, &st) < 0) {
        avahi_log_error("stat(): %s\n", strerror(errno));
        goto fail;
    }

#if 0
    if (!S_ISDIR(st.st_mode) || st.st_uid != pw->pw_uid || st.st_gid != gr->gr_gid) {
        avahi_log_error("Failed to create runtime directory "AVAHI_DAEMON_RUNTIME_DIR".");
        goto fail;
    }
#endif

    r = 0;

fail:
    if (reset_umask)
        umask(u);
    return r;
}
build

configureができたらbuild。
ARM用のstatic linkされたバイナリができていることを確認する。

$ file ./avahi-daemon ./avahi-daemon: ELF 32-bit LSB executable, ARM, version 1 (SYSV), statically linked, for GNU/Linux 2.6.14, not stripped 

$ readelf -d ./avahi-daemon 

 There is no dynamic section in this file.

うまくstatic linkできていないと下記のように表示される。

LDFLAGSが-staticだけのとき
$ readelf -d avahi-daemon
Dynamic section at offset 0x64588 contains 28 entries:
  Tag        Type                         Name/Value
 0x00000001 (NEEDED)                     Shared library: [libdl.so.2]
 0x00000001 (NEEDED)                     Shared library: [libgcc_s.so.1]
 0x00000001 (NEEDED)                     Shared library: [libpthread.so.0]
 0x00000001 (NEEDED)                     Shared library: [libc.so.6]
 0x00000001 (NEEDED)                     Shared library: [ld-linux.so.3]
 0x0000000c (INIT)                       0x9c10
 0x0000000d (FINI)                       0x5efc8
 ...

LDFLAGSが-staticと-static-libgccのとき
$ readelf -d avahi-daemon
Dynamic section at offset 0x66588 contains 27 entries:
  Tag        Type                         Name/Value
 0x00000001 (NEEDED)                     Shared library: [libdl.so.2]
 0x00000001 (NEEDED)                     Shared library: [libpthread.so.0]
 0x00000001 (NEEDED)                     Shared library: [libc.so.6]
 0x00000001 (NEEDED)                     Shared library: [ld-linux.so.3]
 0x0000000c (INIT)                       0x9c64
 0x0000000d (FINI)                       0x608cc
 ...
実行

avahi-daemon/avahi-daemon.confをとりあえずデフォルトのままandroidの/data/avahi/etc/avahi-daemon.confにコピーしておく。あと、httpdのサービスが立ち上がっていると仮定して、/data/avahi/etc/services/http.serviceファイルを作っておく。下記のような感じで。

<?xml version="1.0" standalone='no' ?>
<!DOCTYPE service-group SYSTEM "avahi-service.dtd">

<service-group>
  <name replace-wildcards="yes">%h</name>
  <service>
    <type>_http._tcp</type>
    <port>80</port>
  </service>
</service-group>

あとpidファイルを置くための/data/avahi/var/run/avahi-daemonフォルダを作っておく。(作っておかないと実行時にmkdirできないというエラーが出る)
avahi-daemon実行ファイルもandroidにコピーして実行する。PC(Ubuntu)からavahi-browseでandroidhttpdサービスを見つけることができた。

$ avahi-browse -r _http._tcp -t
+ eth0 IPv4 linux                                         Web Site             local
+ eth0 IPv4 mywebserver                                   Web Site             local
= eth0 IPv4 linux                                         Web Site             local
   hostname = [linux.local]
   address = [192.168.1.200]
   port = [80]
   txt = []
= eth0 IPv4 mywebserver                                   Web Site             local
   hostname = [lynx.local]
   address = [192.168.1.100]
   port = [80]
   txt = []

avahiのインストールはこれでできたっぽい。