OpenBSD Upgrade and Newlisp

とある朝、農道を散歩してたら、前方におばあちゃんがリヤカーにりんご箱を満載して姿を認めた。やや上りぎみの道なんだけど、すいすいと行く。

近頃の老人は元気と言うけど、スーパーおばあちゃんか? 近づいて行くと、リヤカーと言う より、台車にりんご箱が8個積んであった。やや機械音と言うかモーター音が聞こえてきた。

挨拶したけど応答無し。シャイなのか耳が遠いのかは不明。さりげなく観察すると、3輪タイプの台車だった。電動アシスト付きの台車なんて 初めてみるよ。

帰って早速検索してみると、 電動3輪運搬車 パワーキャット

ねこ車だからパワーキャットか。直訳で分かりやすい。農業も年寄り対策で、こういうツールが 売れてるのかな。

8時間充電って、出川が出て来る、充電させてもらえませんか番組よりは、大幅に時間がかかるな。まあ、夜充電すればいいんで、8時間ってのは妥当か。

この猫車って、免許必要なのかな? 電動バイクは必要で、電動アシスト付きママチャリは無免許で 乗れる。その線引きは?

もし免許が必要って事になると、ぼけてますから免許返納と言うか取り消しとかあるんだろうな。ニュースでやってたけど、ぼけで免許取り消しされた人が40人だか50人発生したとか。

オイラー、幸いそんな事態になってないけど、車に乗れなくなったら買い物に困るぞ。 米5Kなんてどうやって運んで来る。やっぱり、山行のリュックサックかな。

OpenBSD Upgrade (6.1 -> 6.2)

もう1ケ月も前に、OpenBSDの版が上がった。年に2回のUpdateを順調に繰り返している。 いつもは、古いのを捨てて、新しい版を入れ、HOMEDIRの引っ越しをやるんだけど、今回は 趣向を変えて、Updateしてみる。

まずは、予行演習と言う事で、Windows7のVMwareに入れてる6.1を上げてみる。 6.2用のISOを取ってくるのは、インストールと同じ。TopページのCurrent Releseの 案内を経由して、How to upgradeへ飛ぶと Upgrade Guide: 6.1 to 6.2に到達 する。Verによって作業が異なるんだろね。

今回の指示は、先にmanを全部消しとけが、オイラーに取って大事な点だ。次はCDから起動して upgradeを選び、指示に従え。終わったら、再起動して、ユーザーランドのパッケージ類を、 add_pkg -u して、更新しとけ、って作業工程。(事前にPKG_PATHを新しい場所にしておく事)これなら引越し要らずで楽ちん。

と思ったら、VMWAREでCDから起動させるにはどうする? 起動した直後にF2キーを押して BIOSに入り、bootデバイスを変更。オイラーこんなF1並みのレースは出来ません。 どうする? 運動音痴用の設定をしろ。設定ファイル OpenBSD.vmx(VMwareの設定ファイル) に

bios.bootDelay="8000"

を書いて、余裕を持ってbiosへ入れましたよ。

upgradeを選ぶと、fsckが走ってから、新しいファイル類をインストール。何も悩む事なく 終了。

新しいので更新って事になるんで、リリース事に番号が振られる libc.so.xx ってのは、 古いものも残っている。よって、ユーザーランドのアプリは、そのまま使える仕掛けだ。 何故、リリース毎にlibc等の版番号を変えるか疑問だったけど、お悩み解消しました。

ちなみに、6.2でのlibcには、libc.so.90.0 な番号が振られていた。若しこの数字が連番で 取られていたら、90回目のリリース。年に2回リリースされるとすると、OpenBSDの歴史は、 45年になる。そんな訳は無いから、中途半端な番号から始まったか、途中でジャンプしてるの だろうね。

気をよくしたオイラーは、Windows10のvboxに入れてるOpenBSDもUpgradeしてみる事に した。CDから起動させる方法がVMwareとは違う。いわゆるBIOS画面が無い代わりに、 マネージャ画面の設定で行う。

ストレージを選び、IDEの空になってるのをクリックする。すると右側の属性でISOファイルを 指定出来るようになる。installcd.isoを選ぶ。次にシステムを選んで、光学にチェックを 入れ、冒頭に持ってくる。こうしておいて起動すればよい。

Updrade中に時差の設定で東京を指定しておくんだけど、それだとJSTになってるけど9時間 進んでる。こういう時は、慌てず騒がず

# config -ef /bsd
OpenBSD 6.2 (GENERIC.MP) #134: Tue Oct  3 21:22:29 MDT 2017
    deraadt@amd64.openbsd.org:/usr/src/sys/arch/amd64/compile/GENERIC.MP
Enter 'help' for information
ukc> timezone -540
timezone = -540, dst = 0
ukc> quit
Saving modified kernel.

でも、再起動すると、元の木阿弥状態になってしまう。これは親分の趣味で、再起動の度に カーネルの再配置 Kernel Address Randomized Link(KARL)が行われる影響だろう。 諦めて、/etc/localtime を削除したよ。 これで、GMTと表示されるけど時刻はローカルタイムという不格好な状態となる。これでも 実害は無いので、このまま使う事にする。

hugsをインストールしようとして、前から保存してたレシピを試したら途中で意味不なエラー になった。これは、portsツリーでもはや保守されていないんで、そんな物は混ぜるな危険と いうサインだろう。

/usr/ports/pobjの下に降りていって、自前でコンパイル。設置場所は、~/.local と言う 目立たない場所にしておいた。以後、野良ビルドしたやつは、ここに置いておこう。 これって、親分が知らない私服を肥やす金庫だな。まあ、見つかる事はあるまい。

最後に、/usr/srcと/usr/portsを削除して、6.2用のそれを入れておいた。上で出て来た、KARLがどう実現されてるか見ておけ。どうやら、/usr/shere/compileの下にリンク用の素材が 置いてある。このdir名をgrepでもして現場を特定すればいいかな。

KARL

KARLの事少し調べてみた。現場は、/usr/src/distrib/miniroot/install.sub の中にある finish_up関数の一部分。 インストール若しくはアップグレードと共通になってる。

        # Write kernel.SHA256 matching the just installed kernel and fix path to
        # ensure it references the kernel as /bsd.
        sha256 /mnt/bsd | (umask 077; sed 's,/mnt,,' >/mnt/var/db/kernel.SHA256)

        if [[ -f $_compile.tgz ]]; then
                echo -n "Relinking to create unique kernel..."
                (
                set -e
                rm -rf $_compile
                mkdir -m 700 -p $_compile
                tar -C $_compile -xzf $_compile.tgz $_kernel
                chroot /mnt /bin/ksh -e -c "cd ${_compile#/mnt}/$_kernel; \
                        make newbsd; make newinstall"
                rm -f $_compile.tgz
                ) >/dev/null 2>&1 && echo "done." || echo "failed."
        fi

arch/amd64/conf/Makefile.amd64の中にnewinstallが有った。ここで、SHA256を書き出している。

newinstall:
        cmp -s bsd /bsd || ln -f /bsd /obsd
        umask 077 && cp bsd /nbsd && mv /nbsd /bsd && \
            sha256 -h /var/db/kernel.SHA256 /bsd

このkernel.SHA256が使われるのは、 libexec/reorder_kernel/reorder_kernel.shのスクリプト中。ここで、sha256コマンドでコンペアされてる。 なお、このスクリプトは、/etc/rc の中から呼び出されてる。比較に失敗しても、さらーっと メッセージが出てくるだけで、注意しないと気が付かない。まあ、ランダムにリンクされるから 悪い事をする人からの、お守り程度かな。

make newbsd は、何処に有る? さんざん探し回って、/usr/shere/compile/GENERIC.MPの 中で見つけた。これって、カーネルをコンパイルする時に、自動生成されるんだね。だから ソースツリーを漁っても出てこない訳だ。

newbsd:
        ${MAKE_GAP}
        ${SYSTEM_LD_HEAD}
        ${SYSTEM_LD} swapgeneric.o
        ${SYSTEM_LD_TAIL}
        mv -f newbsd bsd

更に詳細を追って行くと

SORTR=          sort -R

SYSTEM_LD=      @echo ${LD} ${LINKFLAGS} -o $@ '$${SYSTEM_HEAD} vers.o $${OBJS}\
'; \
                umask 007; \
                echo ${OBJS} param.o ioconf.o vers.o | tr " " "\n" | ${SORTR} >\
 lorder; \
                ${LD} ${LINKFLAGS} -o $@ ${SYSTEM_HEAD} `cat lorder`

で肝になりそうな sort -R のオプションを調べてみると

     -R, --random-sort, --sort=random
             Sort lines in random order.  This is a random permutation of the
             inputs with the exception that equal keys sort together.  It is
             implemented by hashing the input keys and sorting the hash
             values.  The hash function is randomized with data from
             arc4random_buf(3), or by file content if one is specified via
             --random-source.  If multiple sort fields are specified, the same
             random hash function is used for all of them.

ちっともsortらしからぬ挙動を示す事が出来るとな。大体sortって、決められて順番に並び変えるものでしょ。上記オプションはでたらめに並び変えるよって事で、真逆じゃん。そんな機能がsortに潜んでいるとは! これって狭い考えだよ。並び変えるってくくりで捉えるのだ。どう並び変えるかは、オプションで設定する。たまたま、順番に並び変える要求が一番多いので、それがデフォになってるだけ。リナにもこのオプションが付いていたから、POSIXの要求なのかな? それとも、古のunixから有った? 後で調べてみれ。

リンクする時に、オブジェクトファイルの 順番をバラバラにしちゃうと言う、変態(誉め言葉)な事をやっている。

reorder_libs()

更に変態さんは、別の所にも頻出してる。/usr/libの下に有る、 libc.so.90.0と libcrypto.so.42.0も、bootの度に、再配置してる模様。

$ cat libc.0616
SHA256 (/usr/lib/libc.so.90.0) = a04a14c0bc6a0d8e46b37eba2c42eab0b358a278b11ccdf4ee769e8f41061921
$ sha256 /usr/lib/libc.so.90.0
SHA256 (/usr/lib/libc.so.90.0) = 0c4c7e9c678dd19868af732ac71840a11ad67304e97b2c3caffc21d7a8d79e1a
$ sha256 -C libc.0616 /usr/lib/libc.so.90.0
(SHA256) /usr/lib/libc.so.90.0: FAILED

libc.0616は、今朝の6時16分に起動した時のsha256.それと今(午後3時10分)のlibcの shaを比べてみた。両者のhashが異なっている事が分かる。 まあ、この2つのライブラリィーをランダムにしておけば、枕を高くして眠れるってもんです。

この機能は、/etc/rc内で、こっそり行われていたぞ。なんだか、ld.soとldのトリックが 使われているみたいだけど、オイラーの力量では、種明かし出来ず。悔しいなあ。

昔買った、binary hack でもひも解けば、ヒントが得られるかな?それとも、リンカー・ローダー本がいいかな?

取り合えず、広告しときますんで、謎が解けたら、神田明神で絵馬にして奉納してください。

        # Only choose the latest version of the libraries.
        for _liba in /usr/lib/lib{c,crypto}; do
                _libas="$_libas $(ls $_liba.so.+([0-9.]).a | sort -rV | head -1 )"
        done
        _libas=${_libas# }

        for _liba in /usr/libdata/ld.so.a $_libas; do
                _tmpdir=$(mktemp -dq /tmp/_librebuild.XXXXXXXXXXXX) &&
                (
                set -o errexit
                _install='install -F -S -o root -g bin -m 0444'
                _lib=${_liba##*/}
                _lib=${_lib%.a}
                cd $_tmpdir
                ar x $_liba
                if [[ $_lib == ld.so ]]; then
                        ld -g -x -e _dl_start \
                            --version-script=Symbols.map --shared -Bsymbolic \
                            --no-undefined -o ld.so.test $(ls *.o | sort -R)
                        chmod u+x test-ld.so
                        [[ $(./test-ld.so ok) == './test-ld.so: ok!' ]]
                        $_install /usr/libexec/ld.so /usr/libexec/ld.so.save
                        $_install ld.so.test /usr/libexec/ld.so
                else
                        cc -shared -o $_lib $(ls *.so | sort -R) $(cat .ldadd)
                        [[ -s $_lib ]] && file $_lib | fgrep -q 'shared object'
                        LD_BIND_NOW=1 LD_LIBRARY_PATH=$_tmpdir awk 'BEGIN {exit  0}'
                        LD_BIND_NOW=1 LD_LIBRARY_PATH=$_tmpdir openssl \
                            x509 -in /etc/ssl/cert.pem -out /dev/null
                        $_install $_lib ${_liba%/*}/$_lib
                fi
                ) || { _error=true; break; }
        done

余り他人に頼ってはいけません。自分で少し解析してみた。

libcを例にすると、libc.so.90.0 libc.so.90.0.a の2つが対象になる。前者はダイナミックリンク用で後者はスタチックリンク用。タイプは違えど、同一ソースから出来上がっている。

問題は、ダイナミック用の関数の並びをランダムにしたい。で、その関数が定義されてるファイル類を見つけて、ファイルをランダムな順番でリンクすれば、事が足りる。でも、ダイナミック リンクのそれには、残念ながら、ファイル情報が入っていない。

そこで、スタチックリンクに注目。

$ ar x libc.so.90.0.a

すると、スタチックリンクを構成してたファイルがバラバラになる。

$ ls -a
.ldadd
Ovfork.so
Symbols.map
_CurrentRuneLocale.so
_Exit.so
 :
ld.so
 :
ypprot_err.so

これらsoファイルとSymbols.map、.ldaddファイルを使って、再度ダイナミックリンク用ファイルを作り直す。ld.soは、リンカー用のeditorなんだけど、再構成する為の機能に組み替えて いるんだな。(ちょっと自信が無い)

なお、.ldaddは、

-fpic -nostdlib -lcompiler_rt -Wl,--version-script=Symbols.map

リンカー用のオプションをまとめたものだ。

kernelの場合は、*.o ファイルが無いので、インストール時に、/usr/share/compile/に、 それを用意して、再配置に使ってる。libは、*.aを分解すれば、相当品(*.so)が出て来るので それを利用するとな。さすが、裏まで知り尽くした方々だ。

X Window

VMware上のOpenBSDは、Xの起動に失敗する。エラーログから適切な対処が出来ていない。 それより、VMware上では、disk shrinkの方法が無いので、OpenBSDするなら、vbox上の 方が好ましい。(但し、実行スピードは遅いけど)

vbox上では、普通にXが起動してくる。何もマネージャを入れないとtwmという、元祖なやつが 上がってくる。

で、起動時にX loginするにはどうするの? 一応調べておくと、/etc/rc.d/xenodm が走る ように設定しておけばよい。

有名なふぐのイラストがついたログインマネージャが上がってくるぞ。貧乏人はここで、 憧れのふぐを喰って、ふく と成すんだな。

試したついでに、firefoxを入れた。日本語が豆腐になるんで、さざ波フォントを入れた。 それより、DISPLAYの設定が悪さしてて、最初、firefoxが起動しなかった。こういう時は

DISPLAY=:0

とやって、無効にすれば良いのね。知らんかったわい。

newlisp

今まで血圧データの確認用にnewlispで書いたスクリプトを使っていたんだけど、この間の アプリ改修で、PDFに確認データが出るようになった。

よって、Windows10に入れていたnewlispは、もう不要になった。余計なアプリを(Windows10に)入れておくのを良しとしないオイラーは、蛍の光と共に見送ろうと思った。 そうすると、lisp系はWindows10から姿を消す事になる。

ちょいとした計算は電卓って手もあるけど、マウスでぐりぐりが嫌いなオイラーは、やっぱり lisp系をちょびっと入れておきたい。その候補としてGambitCがある。でも、これってフットプリントが50Mと巨大。それに対して、newlispの方は5Mしかない。

一体、newlispはそんな小柄に出来ていて、何を目指していたんだろう? ちょいとFreeBSDの portsの売り文句を調べてみた。

The newLISP is a scripting language for developing web applications and
programs in general and in the domains of artificial intelligence (AI) and
statistics.

WWW: http://www.newlisp.org/

そうか、流行りのWWW系か。ならばネットに繋がる何かとかが平気でサポートされているのだろうな。そう思って、モジュールの中を覗いてみると、キャンバスが有ったりsqlite3が有ったり して、遊びに適していそう。わずかな容量しか喰っていないから、このまま残しておくかな。

newLISP ユーザー マニュアルとリファレンス

ネットを検索したら、有志がきちんとマニュアルを日本語化されてたしね。ええ、日本人贔屓の gaucheだってきちんとしてるぞってのは、承知してますよ。でも、あちらは巨大っぽいからね。 新しい版が出ても、(多分)Windows10には入れない予定。勿論、Debianとかには入れて あげる積りですけどね。

newlisp-mode

Debianにも有るだろうと思ったら、やはり用意されてた。有名なやつなんだな。と言う事は、 きっとemacsからも制御出来るようになってるに違いない。やっぱり有ったので入れておく。

(defvar newlisp-mode-map
  (let ((map (make-sparse-keymap "newlisp")))
    (set-keymap-parent map lisp-mode-shared-map)
    (define-key map (kbd "C-c M-:") 'newlisp-eval)
    (define-key map (kbd "M-C-x") 'newlisp-eval-defun)
    (define-key map (kbd "C-x C-e") 'newlisp-eval-last-sexp)
    (define-key map (kbd "C-c C-b") 'newlisp-eval-buffer)
    (define-key map (kbd "C-c C-r") 'newlisp-eval-region)
    (define-key map (kbd "C-c C-l") 'newlisp-load-file)
    (define-key map (kbd "C-c C-z") 'newlisp-show-repl)
    (define-key map (kbd "C-m") 'newline-and-indent)
    (define-key map (kbd "C-c <f4>") 'newlisp-kill-process)
    (define-key map (kbd "<f5>") 'newlisp-execute-file)
    map))

何という手抜きモード。使えそうな便利関数をソースから引っ張ってきた。C-c C-b ぐらいかなあ、よく使いそうなの。キーにバインドされていないけど、ドキュメントも検索出来るように lookup等が登録されてた。

canvas

canvas.lspを弄ぼうとして開いてみたら、前半はコメント。でも良くみるとhtmlの原稿っぽい。きっとこの原稿を切り出して、htmlファイルを作れるに違いないと思って調べたら、newlispdocがそれだった。

で、サンプルをコピペして実行。

deb9:tmp$ newlisp t.lsp

ERR: problem accessing file : "/usr/local/share/newlisp/modules/canvas.lsp"
called from user function (module "canvas.lsp")

これって、格納場所がBSD仕様じゃないかい。どう修正すれば良い?リンクを貼ってごまかすのが、一番簡単そう。

まてまて、環境変数とかで設定出来ない?マニュアルをざっと見したけど、見つからず。 ならば、それっぽいのが無いか、本体から探してみるか。

deb9:tmp$ strings /usr/bin/newlisp | grep NEWLISP
NEWLISPDIR
(set (global 'module) (fn ($x) (load (append (env {NEWLISPDIR}) {/modules/} $x))))(context 'Tree) (constant 'Tree:Tree) (context MAIN)(define (Class:Class) (cons (context) (args)))

多分NEWLISPって文字列を含むやつが、本体に埋め込まれているはず。探したらそれっぽいのと 、設定部分が出て来た。

deb9:tmp$ export NEWLISPDIR=/usr/share/newlisp/
deb9:tmp$ newlisp t.lsp

ピンポン、当たりましたね。

OpenBSDにも有るかなあと思って探したら、やはり入っていた。と、ここでふと思い出した。 そもそも、Newlispを知ったのは、OpenBSDのportsを見ていた時。一時これにはまっていろいろ とやったな。2015年の事だから、歴史は繰り返す。

昔書いたコードもWebに保存してたんで、DLして遊んでみるかな。

OpenBSDにfirefoxが入ったので、こいつのキャンバスに絵を書かせてみたけど、なんとなく もっさりしてる。Windows10のnewlispを残す事にしたので、そちらでやろう。

newlisp-modeを入れて、モジュールを入れて、快適に動いている。 (~AppDataRoaming.emacs.d 内にinit.elが有るのを探すに苦労したのは秘密だ。それから、ostypeが Windowsに変更になったのも知らんかった。)

 (module "pc.lsp")               ; use this module

 (pc:html "<h3>Lissajous</h3>")  ; we can use html tag
 (pc:canvas 500 500)             ; use real canvas (canvas width height)
 (pc:vbox -1 -1 1 1)             ; vertual canvas  (left bottom right top)
 (pc:move (cos 0) (sin 0))       ; pen up to (move x y)
 (dotimes (i 361)
    (set 't  (mul i (div 3.141592653  180.0)))
    (pc:draw (cos (mul 3 t)) (sin (mul 5  t)))) ; pen down and (draw x y)

 (pc:line-color 1.0 0.1 0)                      ; (line-color R G B)
 (pc:move -1 0) (pc:draw 1 0)
 (pc:move 0 -1) (pc:draw 0 1)

 (pc:render)                        ; firefox launch
 (exit)

実キャンバスのサイズは、500x500。これに対してスクリプトが扱う座標範囲をvboxで 指定する。上の例だとリサージュ波形なので、X,Y軸共、振れる範囲は±1を指定してる。

renderに引数が無いと、firefoxが起動して描画される。ファイルを指定すると、そこに Javascript混じりのhtml内容が吐き出されるとな。やっと、昔を思い出したよ。

HHKのあの先生のWebは最近更新が無いけど、お元気なのかしら。先生はlispとpostscriptが 有れば生きていけるとおっしゃっていたけど、オイラーは、Windowsでも動くnewlispが 有れば幸せだな。