BSD界隈四方山話

第31回FreeBSDのソースコードを調べる方法

ソースコードを調べる

FreeBSDでなにか問題が発生したり解決したいことがでてきた場合には、オンラインマニュアルを読んだりFreeBSDハンドブックを読んだり、またはGoogleで検索して同じ問題とその解決方法が提示されていないか調べることがあります。こうした作業で問題が解決すれば御の字です。

しかし、希なケースの問題であったり、オンラインマニュアルに記載されていない場合、解決策を見つけるのが難しいです。オンラインマニュアルに書いてあることも常にソースコードの記述を正確に反映しているとは限らないため、そうした場合にはソースコードを調べる必要があります。

FreeBSDでソースコードを調べる方法はいくつかあります。ここでは3つの方法を採り上げて、どうやってソースコードを調べていくのか紹介します。想定するシナリオは、lpr(1)コマンドで印刷を行おうとしたところ「unable to get official name ~」といったエラーが出力されて印刷されないという問題が発生したというものです。

Subversionでソースコードをチェック

FreeBSDのソースコードはSubversionで管理されています。基本的にはSubversionを使って調査したい対象となるバージョンのFreeBSDのソースコードをダウンロードしてきて、grep(1)コマンドを使って全文検索を行います。現在のマシンは高速なので、FreeBSDのソースコード量であればgrep(1)による検索で実用的に利用できます。

まず、pkg install subversionのようにしてSubversionをインストールするか、システムにデフォルトでインストールされているsvnliteを使って対象となるバージョンのソースコードを取得します。svnliteを使う場合、svnコマンドの部分をsvnliteに置き換えてコマンドを実行してください。

svn(1)コマンドでFreeBSDのソースコードを取得
# FreeBSD 10.2系のソースコードを取得
svn checkout https://svn.FreeBSD.org/base/releng/10.2 ./

# FreeBSD 9.3系のソースコードを取得
svn checkout https://svn.FreeBSD.org/base/releng/9.3 ./

# FreeBSD 10.2-RELEASEのソースコードを取得
svn checkout https://svn.FreeBSD.org/base/release/10.2.0 ./

# FreeBSD 9.3-RELEASEのソースコードを取得
svn checkout https://svn.FreeBSD.org/base/release/9.3.0 ./

# 開発版のソースコードを取得
svn checkout https://svn.FreeBSD.org/base/head ./

# 10系安定版のソースコードを取得
svn checkout https://svn.FreeBSD.org/base/stable/10 ./

# 9系安定版のソースコードを取得
svn checkout https://svn.FreeBSD.org/base/stable/9 ./

あとは何も考えずにキーワードでgrep(1)検索を行います。次のようにlpr(1)コマンドのソースコードの1つである「common_source/net.c」というファイルの中に該当する部分があることがわかります。

grep(1)による全文検索で対象の絞り込み
# cd /usr/src/
# grep -r 'unable to get official' *
usr.sbin/lpr/common_source/net.c:        asprintf(&error, "unable to get official name "
#

該当する部分をみると、checkremote()という関数の実装の中で、getaddrinfo(3)というライブラリ関数がエラーを返していることが原因になっていることがわかります。

問題が発生していた部分を特定
# cat -n usr.sbin/lpr/common_source/net.c
…略…
   171  /*
   172   * Figure out whether the local machine is the same
   173   * as the remote machine (RM) entry (if it exists).
   174   * We do this by counting the intersection of our
   175   * address list and theirs.  This is better than the
   176   * old method (comparing the canonical names), as it
   177   * allows load-sharing between multiple print servers.
   178   * The return value is an error message which must be
   179   * free()d.
   180   */
   181  char *
   182  checkremote(struct printer *pp)
   183  {
   184      char lclhost[MAXHOSTNAMELEN];
   185      struct addrinfo hints, *local_res, *remote_res, *lr, *rr;
   186      char *error;
   187      int ncommonaddrs, errno;
   188      char h1[NI_MAXHOST], h2[NI_MAXHOST];
   189
   190      if (!pp->rp_matches_local) { /* Remote printer doesn't match local */
   191          pp->remote = 1;
   192          return NULL;
   193      }
   194
   195      pp->remote = 0;  /* assume printer is local */
   196      if (pp->remote_host == NULL)
   197          return NULL;
   198
   199      /* get the addresses of the local host */
   200      gethostname(lclhost, sizeof(lclhost));
   201      lclhost[sizeof(lclhost) - 1] = '\0';
   202
   203      memset(&hints, 0, sizeof(hints));
   204      hints.ai_family = family;
   205      hints.ai_socktype = SOCK_STREAM;
   206      hints.ai_flags = AI_PASSIVE;
   207      if ((errno = getaddrinfo(lclhost, NULL, &hints, &local_res)) != 0) {
   208          asprintf(&error, "unable to get official name " ← ここにヒットしてる
   209               "for local machine %s: %s",
   210               lclhost, gai_strerror(errno));
   211          return error;
   212      }

ようするに名前解決に失敗している、ということになります。名前解決には/etc/resolv.confや/etc/hosts、/etc/host.conf、/etc/hosts.*ファイルあたりが関係しています。/etc/printcapで指定したホスト名が間違っていたのかもしれません。

/etc/resolv.confで指定できる
# cd /usr/src/
# grep -r MAXNS * | grep define
include/resolv.h:#define    MAXNS          3  /*%< max # name servers we'll track */
#

実はこれは実際にあった問題で、/etc/resolv.confでnameserverの指定が4行あったことが原因です。/etc/resolv.confで指定できるnameserverの数はMAXNSまでですので、現在のところ3行までとなっています。このため、解決できる名前と解決できない問題がでていて、問題を特定することが難しく、ソースコードをまで調べることになった、という状況だったりします。

svnwebでソースコードをチェック

すでにどこのソースコードを読めばよいか検討がついているという場合には、http://svnweb.freebsd.org/を使うという方法もあります。FreeBSDのSubversionリポジトリの中身をそのままWebブラウザから閲覧できますし、変更履歴も見ることができます。

 http://svnweb.freebsd.org/
図 http://svnweb.freebsd.org/

Subversionを利用するのが面倒な環境を使っているとか、新しいソフトウェアのインストールが許されていない環境やデバイスを使っている場合なんかに使える方法です。ただ、これはある程度中身が把握できていないと難しいところがあるので、通常のユーザであれば次の紹介するGitHubを使う方法がよいでしょう。

GitHubでソースコードをチェック

FreeBSDのソースコード自体はSubversionで管理されていますが、GitHubへのエクスポートも実施されているので、ソースコードや開発の履歴そのものをGitHubで追っかけるといったことができます。今の開発者的にはこの方法が一番なじみやすい方法でしょう。

 FreeBSDソースコード on GitHub
図 FreeBSDソースコード on GitHub

GitHubはソースコードの検索サービスを提供していますので、grep(1)検索で指定したのと同じキーワードを指定することで同じようにソースコードを絞り込むことができます。検索性能はこちらの方が柔軟でかつ高速です。

 GitHubで検索
図 GitHubで検索
 もちろんソースコードを読める。ブラウザの機能で検索もできる
図 もちろんソースコードを読める。ブラウザの機能で検索もできる

最近、FreeBSDのエンジニアはGitHubの有用性を高く評価しています。今のところソースコードの管理方法自体をSubversionからGitHubに変更する動きは見られませんが、多くのエンジニアがGitHubの便利さを理解していることは間違いのないところです。

ソースコードは情報のかたまり

何も知らない状態ですとFreeBSDハンドブックのようにある程度のクオリティでまとめられた文章が有益ですし、ちょっと詳しくなってきたらオンラインマニュアルが役に立つと思います。しかしながら、そこから先はソースコードを読んだ方が近道です。

ソースコードの大半はC言語で記述されていますので、C言語が読めないと難しいところがありますが、コメントやマクロなどを追うだけでも多くの情報を得ることができます。GitHubを使う方法なんかは入りやすいところだと思いますし、ぜひ一度試してみてください。

おすすめ記事

記事・ニュース一覧