僕は FreeBSD は調べたい時にしか使わない。そのために日常的に1台の PC を FreeBSD に割り当ててない。FreeBSD がインストールされたハードディスクがあるのみである。近年のハードディスクは SATA のコネクターになって容易に交換できる。あの取り外しが面倒な IDE から SATA に変わった時には本当にありがたいと思った次第である。
今回、数年前にインストールした FreeBSD を立ち上げてみると、古い! バージョンアップが必要であった。この作業が結構面倒だと気付いた。そこで新規インストールし直した。ここでは、これに伴って僕が経験した事、感じた事をまとめておく。僕の個人的な忘備録でもある。
FreeBSD は僕の家庭内 LAN で動いている。家庭内 LAN では僕が設定している DHCP サーバーが動いている。わざわざ自前の DHCP サーバーを動かす理由は、我が家の LAN の中では多数のサーバーが動いており、それらに固定 IP を割り当てて名前でアクセスしたいからである。そのためにはサーバーの MAC アドレスさえ分かればよい。今回新規インストールした FreeBSD 自体は特にサーバーとしての設定はしていない。ネットワークに関してはデフォルトのままである。IPアドレスはDHCPサーバーから受け取っている。この事は労力の大きな節約になる。
筆者が普段文房具代わりに使っているコンピューターは MacBook である。近年は Mac の利用者が増え、unixに関心を持つ人であれば大抵は Mac を持っているのではないかと思う。ここで Mac を話題にするのは、以下の話の中で Mac が大いに関係しているからである。
新しい OS は USB メモリーからインストールするのが一番簡単である。僕は最初は MacBook に FreeBSD のイメージをタウンロードして SATA ハードディスクに直接インストールしようとしたが、MacOS のガードが固くて諦めた。FreeBSD インストール用の USB メモリーなら簡単に作れる。以下はその時の記録である。
https://www.freebsd.org
diskutil list
% diskutil list /dev/disk0 #: TYPE NAME SIZE IDENTIFIER 0: GUID_partition_scheme *480.1 GB disk0 1: EFI EFI 209.7 MB disk0s1 2: Apple_HFS root 479.2 GB disk0s2 3: Apple_Boot Recovery HD 650.0 MB disk0s3 /dev/disk2 #: TYPE NAME SIZE IDENTIFIER 0: FDisk_partition_scheme *4.0 GB disk2 1: 0x39 3.9 GB disk2s1これから
/dev/disk2
に割り当てられたことが分かる。dd if=FreeBSD-11.0-RELEASE-amd64-memstick.img of=/dev/disk2 bs=1m conv=sync
diskutil list
インストールはインストール先の PC の BIOS を、USB HDD から立ち上がるように設定し、リブートする。
インストール時の設定の要点を次に示す。
(a) IPアドレスの設定は DHCP サーバーに任せる
(b) ssh による外部からのアクセスを許す
(c) wheel グループに参加する # root 特権を利用するに必要
はっきり言って、面倒な初期設定を避けて、利用は全て Mac から行うようにすればよい。
ssh による外部アクセス以外はサーバーとしての動作は(当面)行わないので、クライアント設定で構わない。
X-Window System も使わない。
FreeBSD の GUI 環境はお世辞にも良いとは言えない。Mac から ssh と sshfs を使って FreeBSD にアクセスする方が遥かに使い心地が良いのである。もしも Mac を持っていれば、FreeBSD をクライアント環境として実用的に利用したいとは誰も思わないであろう。(同様なことは Linux についても言える。)
苦労して GUI 環境の構築に時間を割く価値は無い。
FreeBSD をインストールした PC の名前は bsdである。ssh によるアクセスは
ssh arisawa@bsd
この場合 FreeBSD 上で実行できるのはコマンドだけである。(マウスが使えない)
簡単なテキストエディタ edit が付属している。簡単だがちょっとした編集を行うに十分な機能を持っている。
コマンドを使うメリットには、(マウスによるGUIに比べて)行ったことの記録が取りやすいことがある。あの時に何をどのように行ったか? この記録はコマンドだと非常に簡単だが、マウスを使い出すと大変なことになる。しかもアプリケーションの GUI 環境は変更が多い。多すぎる!
Mac から ssh でリモートアクセスしている場合には、コマンドの記録は簡単に取れる。Copy&Paste で Mac のテキストファイルに必要な記録を取っていけばよい。こうした記録は後の利用の場合に大いに役に立つし、自動化のための新しいコマンドの作成に活用できる。
sshfs によって、FreeBSD を Mac の一部であるかのように扱うことができる。具体的には FreeBSD のファイルシステムを Mac の Finder でブラウズでき、FreeBSD のファイルを Mac のテキストエディタで編集できる。さらに FreeBSD と Mac の間のファイル転送もマウスの Drag&Drop でやっていける。
Mac から FreeBSD への sshfs は十分に安定しているように見える。大きなファイルも問題なくやりとりできる。
FreeBSD をインストールした PC の名前は bsdである。そこで僕は次のようにマウントしている。
sshfs arisawa@bsd:/ /n/bsd
/n/bsd
以下に見えることになる。/n/bsd
をあらかじめ作成しておく必要がある。
ディレクトリ /n
の中にリモートホストのマウント場所を持つのは Plan9 の習慣である。unix では通常(共同利用を想定して) /mnt
にマウント場所を設定するのが習慣であるが、個人が独占的に使用するコンピュータの場合には /n
で構わない。僕はその方が使いやすい。
インストール時のディレクトリ構成は次の通りである。
$ ls -l / total 77 -r--r--r-- 1 root wheel 6197 Sep 29 10:45 COPYRIGHT drwxr-xr-x 2 root wheel 1024 Sep 29 10:44 bin drwxr-xr-x 8 root wheel 1536 Mar 5 19:52 boot dr-xr-xr-x 10 root wheel 512 Mar 18 17:13 dev -rw-r--r-- 1 root wheel 4096 Mar 18 08:13 entropy drwxr-xr-x 25 root wheel 2560 Mar 6 09:35 etc lrwxr-xr-x 1 root wheel 8 Mar 5 19:32 home -> usr/home drwxr-xr-x 4 root wheel 1536 Sep 29 10:44 lib drwxr-xr-x 3 root wheel 512 Mar 5 18:24 libexec drwxr-xr-x 2 root wheel 512 Sep 29 10:43 media dr-xr-xr-x 2 root wheel 512 Sep 29 10:43 proc drwxr-xr-x 2 root wheel 2560 Sep 29 10:44 rescue drwxr-xr-x 2 root wheel 512 Mar 17 07:46 root drwxr-xr-x 2 root wheel 2560 Sep 29 10:45 sbin lrwxr-xr-x 1 root wheel 11 Sep 29 10:45 sys -> usr/src/sys drwxrwxrwt 7 root wheel 512 Mar 29 03:10 tmp drwxr-xr-x 16 root wheel 512 Mar 5 19:52 usr drwxr-xr-x 25 root wheel 512 Mar 18 17:13 var $
僕のホームディレクトリは
/home/arisawa
$HOME
として設定されている。また /usr
の内容は次の通りである。
$ ls -l /usr total 92 drwxr-xr-x 2 root wheel 8192 Mar 5 18:24 bin drwxr-xr-x 3 root wheel 512 Mar 5 19:32 home drwxr-xr-x 55 root wheel 6656 Sep 29 10:45 include drwxr-xr-x 10 root wheel 15360 Sep 29 10:45 lib drwxr-xr-x 4 root wheel 15872 Mar 5 18:24 lib32 drwxr-xr-x 6 root wheel 512 Sep 29 10:43 libdata drwxr-xr-x 8 root wheel 1536 Mar 5 18:24 libexec drwxr-xr-x 17 root wheel 512 Mar 6 22:07 local drwxr-xr-x 2 root wheel 512 Sep 29 10:43 obj drwxr-xr-x 69 root wheel 1536 Mar 5 11:10 ports drwxr-xr-x 2 root wheel 5632 Sep 29 10:45 sbin drwxr-xr-x 33 root wheel 1024 Sep 29 10:45 share drwxr-xr-x 23 root wheel 1024 Mar 17 02:21 src drwxr-xr-x 15 root wheel 512 Mar 5 18:26 tests
FreeBSD のプログラムソースは /usr/src
と /usr/ports
に置かれている。
OS 標準の基本的なソフトウェアのソースプログラムは /usr/src
以下に含まれている。それ以外のものはユーザーの好みに応じて後からインストールすることとなる。バイナリ配布で構わないなら pkg
を、ソースレベルで欲しいなら ports
を使う。使い方に関してはネットに詳しい解説がある[1]。僕は pkg
には関心が無い。
或るコマンド、例えば sysinfo
が利用できるか否かを調べるには whereis
コマンドで調べればよい。
この場合には
$ whereis sysinfo sysinfo: /usr/ports/sysutils/sysinfoを得る。このケースではインストールすれば使えることが分かる。
$ cd /usr/ports/sysutils/sysinfo $ ls Makefile distinfo pkg-descr pkg-plist $ su Password: root@bsd:/usr/ports/sysutils/sysinfo # make install
ディレクトリ /usr/ports/sysutils/sysinfo
の中にはインストールに必要な最小限の情報しか含まれていない。プログラムの本体はネットを通じて取り寄せられ、コンパイルされる。
さて、sysinfo
をインストールすると /usr/ports/sysutils/sysinfo
の下にディレクトリ work
が作成され、その下には stage
と sysinfo
が作成される。
/usr/ports/sysutils/sysinfo/work/stage/ /usr/ports/sysutils/sysinfo/work/stage/usr/ /usr/ports/sysutils/sysinfo/work/stage/usr/local/ /usr/ports/sysutils/sysinfo/work/stage/usr/local/bin/ /usr/ports/sysutils/sysinfo/work/stage/usr/local/etc/ /usr/ports/sysutils/sysinfo/work/stage/usr/local/etc/devd/ /usr/ports/sysutils/sysinfo/work/stage/usr/local/etc/libmap.d/ ... /usr/ports/sysutils/sysinfo/work/sysinfo/ /usr/ports/sysutils/sysinfo/work/sysinfo/ /usr/ports/sysutils/sysinfo/work/sysinfo/LICENCE /usr/ports/sysutils/sysinfo/work/sysinfo/common.subr /usr/ports/sysutils/sysinfo/work/sysinfo/doc/ ...
/usr/ports/sysutils/sysinfo/work/sysinfo
は作業用らしい。
/usr/ports/sysutils/sysinfo/work/stage
はインストールイメージで、make
で install
を指定しなければ stage
以下を構成して終わるらしい。実際にインストールする前にどこに何がインストールされるか確認できるわけだ。良いアイデアだと思う。stage
にはたくさんのフォルダやファイル名が登場するが、ファイルサイズが正のファイルのみに関心を払えばよい。ディスク容量を幾分余計に食うが、安くなったから節約する必要はないのだろう。安全第一。
[1] About FreeBSD Ports
http://www.FreeBSD.org/ports
[2] Ports Collection の利用
https://www.freebsd.org/doc/ja_JP.eucJP/books/handbook/ports-using.html#ports-upgrading-tools
[3] Stage Directory support for ports
https://wiki.freebsd.org/ports/StageDir
単に sysinfo
だとヘルプメッセージが表示される。
全ての情報を表示するには
sysinfo -a
テーマを選択することもできる。以下は出力例である。
$ sysinfo network Generated by SysInfo v1.0.1 by Daniel Gerzo Network information hostname: bsd Currently available network devices: em0 lo0 Basic configuration for currently available NICs: em0 (bsd.local): status: active em0 is configured via DHCP MAC address: 68:05:ca:00:fc:34 IPv4 addresses: 192.168.0.2 netmask 0xffffff00 IPv6 addresses: fe80::6a05:caff:fe00:fc34%em0 prefixlen 64 2402:6b00:4040:b600:6a05:caff:fe00:fc34 prefixlen 64 WARNING: /dev/mem is not readable! WARNING: Running /usr/local/share/sysinfo/modules/network as an unprivileged user may prevent some features from working. lo0 (localhost): IPv4 addresses: 127.0.0.1 netmask 0xff000000 IPv6 addresses: ::1 prefixlen 128 fe80::1%lo0 prefixlen 64 WARNING: /dev/mem is not readable! WARNING: Running /usr/local/share/sysinfo/modules/network as an unprivileged user may prevent some features from working. INFO: Check ifconfig(8) for more information. Default route: WARNING: /dev/mem is not readable! WARNING: Running /usr/local/share/sysinfo/modules/network as an unprivileged user may prevent some features from working. Firewall related information: WARNING: inetd(8) is enabled, however none of its services are enabled. Check /etc/inetd.conf. There are 15 rules in /etc/hosts.allow (-rw-r--r--) which affect inetd(8) operation and services linked against libwrap. INFO: Check the hosts_options(5) manual page for more information. WARNING: No firewall is being used on this system. INFO: There are several firewall packages on FreeBSD. INFO: You may want to check http://www.freebsd.org/doc/en/books/handbook/firewalls.html. Resolver name servers: 192.168.0.5 Socket statistics: There are currently 9 listening (5 TCP/4 UDP) and 2 established connections. INFO: For more information please see sockstat(8) manual. $
なお、sysinfo
コマンドは 9front にも存在するが1、9front
の方は、システム情報を出力したコマンドもコメントとして残している。
標準添付。emacs 風のテキストエディタ。結構使いやすい。通常の作業はこれで十分である。
ports からコンパイルする必要がある。しかし、どうした訳かコンパイルエラー。もっとも僕は使わないが...
jove は emacs 使いにも違和感なく使える小さなテキストエディタである。最近は(マウスが使えない環境では)もっぱらこれを愛用している。ASCII 文字にしか対応していないが、システム管理にはこれで十分である。
jove は僕が昔 microware の OS9 用に作ったテキストエディタ em にそっくりである。僕は unix が日本に上陸した頃からの unix ユーザーである。そして emacs に惚れ込んだ。何しろ(vi に比べれば)オペレーションが自然で、強力で...
しかし emacs はデカすぎて僕のパソコンに載らなかった。何しろ当時はまだ 8-bit の時代であった。そこで emacs のように振る舞うテキストエディタを作ったのである。もちろん Lisp ではなく、C で書かれていた。"em" の名称は、小さな emacs から来ている。それ以来、僕はもう emacs は使わない。現在の emacs は、操作が煩雑になり、僕は嫌いになった。
jove は Linux でも macOS でも動く。標準添付ではないのでインストールが必要である。
/bin/sh
。su
で実行されるシェルは何故か /bin/csh
になっている。su -m
標準 C コンパイラは clang である。gcc は ports を通じてインストールする必要がある。
しかし僕は特に gcc に魅力を感じていない。
GNU のソフトは嫌いである。理由は、詰まらぬオプションや拡張の山だからである。
GNU はそれによって意図的に非互換性を持ち込んでいるようにすら思える。
Plan9port と言うのは Russ Cox が Plan9 のツールを unix に輸出した一群のファイルである。現在、Linux、FreeBSD、OpenBSD、MacOS がサポートされている。
僕にとっては Plan9port は是非とも使いたいツール群である。
Plan9port も FreeBSD ではコンパイルエラーになる。
試したのは code.google.com の最新版(2014-03-06版)で、これは Russ Cox が管理している最後の版だと思われる。Russ Cox は以後 Plan9port を Github に上げている。Github の最新版でも同様にエラーになる。
主な原因は FreeBSD の標準コンパイラが gcc から clang に変わったことによる。
これは次のように(何とか)回避できる。
以下、orig
のサフィックスを添えたものはオリジナルファイルで、添えられていないのは修正後のファイルである。
$ pwd /usr/local/plan9/bin $ diff -u 9c.orig 9c --- 9c.orig 2017-03-08 03:57:13.378293000 +0900 +++ 9c 2017-03-06 09:09:39.000000000 +0900 @@ -77,6 +77,7 @@ case "$tag" in *FreeBSD*gcc*) usegcc ;; *FreeBSD*clang*) useclang ;; +*FreeBSD*) useclang ;; *DragonFly*|*BSD*) usegcc ;; *Darwin-x86_64*clang*) useclang $ diff -u 9l.orig 9l --- 9l.orig 2017-03-06 00:09:36.101612000 +0900 +++ 9l 2017-03-06 09:30:37.000000000 +0900 @@ -12,14 +12,15 @@ tag="${SYSNAME:-`uname`}-${OBJTYPE:-`uname -m`}" case "$tag" in *FreeBSD*) - ld=${CC9:-gcc} + #ld=${CC9:-gcc} + ld=${CC9:-cc} userpath=true extralibs="$extralibs -lutil" case "`uname -r`" in 5.2.*) extralibs="$extralibs -lkse" ;; - [5-9].*) + [5-9].* | 10.* | 11.*) extralibs="$extralibs -lpthread" ;; esac $ cd /usr/local/plan9/src/cmd/auxstats $ mv FreeBSD.c FreeBSD.c.orig $ cp OpenBSD.c FreeBSD.c # NB: OpenBSD.c is dummy file.
[4] Russ Cox: Plan 9 from User Space
https://swtch.com/plan9port/
https://github.com/9fans/plan9port
https://code.google.com/archive/p/plan9port/downloads
# 2014-03-06
今回改めて気づいたことだが、FreeBSD の(C言語の)コーディングスタイルが Plan9 流になっている。
例えば /usr/src/lib/libc/string/strcpy.c
の中には関数 strcpy
の定義が含まれるが
char * strcpy(char * __restrict to, const char * __restrict from) { char *save = to; for (; (*to = *from); ++from, ++to); return(save); }
図1: 標準コーディングスタイル
と書かれている。調べてみると、既に 4.4BSD のコードでもこのようになっている。
入門本などでは次のように書かれているであろう1。
char *strcpy(char * __restrict to, const char * __restrict from) { char *save = to; for (; (*to = *from); ++from, ++to); return(save); }
図2: 入門本に見られるコーディングスタイル
2つを比較すると、後者の方が分かりやすいと多くの人が感じるのではないだろうか?
このことが入門本の中では後者のスタイルを採用する理由であろう。
しかし、機能的にはどうか? プログラミング作業を考えてみると、前者の方が優れていることがわかる。プログラムを作っていると、あるいはプログラムを解析していると、関数定義がどこで行われているかを知る必要性が頻繁に発生するのである。膨大なコードの中で strcpy
の定義部を見つけなくてはならない。前者のように書かれていれば
grep -n '^strcpy(' *.c
簡単に探せるようにするには、スタイルを統一しなくてはならない。外れているものが存在すると、探索パターンは手に負えないくらい複雑になってしまう。
なお、「入門本」と書いたが、本格的なプログラミング本、例えばソフトバンクから出ている「詳解 UNIX プログラミング」とか「詳解 UNIX ネットワークプログラミング」では図1のスタイルで書かれている。
[5] B.W.Kernighan and D.M.Ritchie (石田晴久訳)「プログラミング言語C 第2版(ANSI規格準拠)」
1993,共立出版
[6] B.W.Kernighan and Rob Pike "The Practice of Programming"
Lucent Technologies, 1999
4th printing 2001, Addison-Wesley
FreeBSD に標準添付されているソースコードは膨大で、/usr/src
の中に含まれている C のソースプログラムだけでも 19000 もある。下準備が必要である。
ファイルの一覧が欲しい。
ファイルの一覧を出力する unix の代表的なプログラムは ls
である。しかし
ls -1R /usr/src
grep
ではファイルのパスは探せないのである。
欲しいのは次の形式の一覧である。
/usr/src/ /usr/src/.arcconfig /usr/src/.arclint /usr/src/COPYRIGHT /usr/src/LOCKS /usr/src/MAINTAINERS /usr/src/Makefile /usr/src/Makefile.inc1 /usr/src/Makefile.libcompat /usr/src/ObsoleteFiles.inc /usr/src/README /usr/src/UPDATING /usr/src/bin/ /usr/src/bin/Makefile /usr/src/bin/Makefile.inc ...
図3: 欲しい出力
この形式ならばファイル名を与えて簡単に場所がわかる。find
はかなり近いことをやってくれるのだが、デイレクトリパスの末尾に "/
" が付いていない。例えば /usr/src/
ではなく、/usr/src
となるのは困る。find
には膨大なオプションがあるので、何とかなるのかも知れないが、それくらいはデフォルトでやって欲しい。
僕が Plan9port をインストールしたのは、このようなファイル一覧を作成してくれるツール lr
を既に作って持っていたからである。実際に必要とされるのはファイルだけなので
lr /usr/src | grep -v '/$' > $HOME/db/src
ついでに言えば、unix の素晴らしい点は、プログラムの出力が他のプログラムの入力として設計しやすいことである。それによって単機能の小さな(従って開発しやすい)プログラムを書いて、あとは既存のツールと組み合わせて、さっさと目的を達成できることになる。
ところが現実のツールは眉を顰めたくなることがある。
コマンド ls
の出力は頂けない。ls -l
は date 表示が悪すぎる。また ls -1R
の出力も悪い。この出力からファイルのバスを見つけるには(ls -1R
の出力に特化した)特殊なツールが必要になる。unix には膨大なフリーなツールが存在するので、そのようなツールはどこかにあるのかも知れないが探すのが大変である。それよりも ls -1R
がシンプルに図3のように出力してくれた方が grep の入力として扱いやすい。
コマンド find
は膨大なオプションを持っている。まるで多くのオプションを持つことは良いことだと言わんばかりである。マニュアルを読むだけで頭が痛くなる。パイプと grep
を組み合わせれば簡単に済むことがコマンドのオプションとしてサポートされているのである。こうした詰まらぬオプションを多数持つよりも、パイプと grep
の使い方をユーザーに教える方がよほど建設的ではないか?
これで、或るファイル、例えば strcpy.c を見つけたいならば
grep strcpy.c $HOME/db/src
find
でもやっていけるだろうが、僕は膨大なオプションの説明を含む find
のマニュアルを読みたくもない。
Cのプログラムの一覧は
grep '\.c$' $HOME/db/src > $HOME/db/src_c
src_c
の中から何か或る関数(例えば foo)をどのように見つけるかだ。
unix には、この場合に便利な xargs
コマンドがある2。関数 foo
が図1のスタイルで書かれていれば
cat $HOME/db/src_c | xargs grep '^foo('
残念ながら FreeBSD のコーディグスタイルは完全には統一されていない。基本ルールから外れているスタイルを探し出そう。
$ cat src_c |xargs grep '^[a-zA-Z0-9_][a-zA-Z0-9_ ]* *(.*)[^;]*$' src src_c|grep -v ':[a-zA-Z0-9_]\+('|p /usr/src/bin/sh/arith_yacc.c:static __dead2 void yyerror(const char *s) /usr/src/bin/sh/arith_yacc.c:static arith_t arith_lookupvarint(char *varname) /usr/src/bin/sh/arith_yacc.c:static inline int arith_prec(int op) /usr/src/bin/sh/arith_yacc.c:static inline int higher_prec(int op1, int op2) /usr/src/bin/sh/arith_yacc.c:static arith_t do_binop(int op, arith_t a, arith_t b) /usr/src/bin/sh/arith_yacc.c:static arith_t primary(int token, union yystype *val, int op, int noeval) /usr/src/bin/sh/arith_yacc.c:static arith_t binop2(arith_t a, int op, int precedence, int noeval) ...気が遠くなるほど膨大なコードが引っかかる3。特にユーザーからの寄贈品や他のOSから持ってきたもの、古いコードに問題がある。
注意しておくが、これで完全に探し出せた保証はない。C言語はどこまでも変なスタイルのコーテーィングが可能なので、完全に取り出すのは grep
では手に負えない。コーディグスタイルを守らない限り、ちゃんとしたパーサーが必要なのである。
locate
コマンドが存在する。locate strcpy.c
strcpy.c
の置き場所を探してくれる。もっともタイムラグがあり、FreeBSD ではラグは1週間である。Linux では1日に改善されており、さらに必要に応じて手動で locate データベースを更新できる。sudo launchctl load -w /System/Library/LaunchDaemons/com.apple.locate.plist
locate
が使えるようになる。xargs
も眉を顰めたくなる。このコマンドの使い方が複雑になっていく原因は xargs
が入力の1行に複数のファイルパスを許し、その上、制御コードを含むファイル名をも許しているからである。不必要な欲張りであり、プログラムを複雑にし、しかも扱い難くする。僕は xargs
の代替えとしてPlan9 用に 9xa
を持ち、unix に移植して使っている。9xa
について詳しくは文献[7]を見よ。
[7] Kenji Arisawa: xargs and 9xa
http://ar.nyx.link/blog/9xa/