スーパー・カッコ
============= 怪 =============
暫く火を入れていなかったdebian機をupdateしようとしたんだ。そしたら、ネットに繋がっていなかったよ。
WIFIのインジケーターを見たら、消灯してる。ハードの認識に失敗したのかも知れん。一度火を落として、もう一度やってみるか。やっぱりインジケーターは点灯していない。コマンド叩いて、NICが生きているか調べる。ノーキャリアだって。オイラーと同じだな。
長年頑張ってきたけど、とうとういかれたか。
まて、わけわかめなリナ止めて、最後にUSBスティックに入れてる、OpenBSDで確認してみるか。
ブート画面にこんなメッセージが現れた。
: starting network iwn0: radio is disabled by hardware switch iwn0: SIOCSIFFLAGS: Operation not parmitted iwn0: no link ......... sleeping :
一目瞭然、スイッチが切れてますって。そんなスイッチ何処にある? 探し回って、右サイドに こっそり隠れてるやつを見つけた。そしてそれが、確かにOFFになってた。スイッチが中途半端な位置にあるわけじゃなくて、きちんとOFFのポジション。
大体、使ってる本人がスイッチを失念してるぐらいだから、触れた記憶なんて全くない。 女房に聞いても、恐い物には触らないと言う。(最近はすっかりipad利用者)
はて、座敷わらしが夜中に出て来て、いたずらしてった?(by柳田邦男先生の遠野物語説) オイラーが霊に操られて、操作しちゃった?(ゾンビ症候群説)この所、リナをいじっていないので、リナの守り神が嫉妬した? 物の怪である。
いずれにしろ、OpenBSDの守護神であるダエモン君のおかげで、事なきを得た。感謝しろよ。
リナのマスコットはペンギン野郎、OpenBSDはふぐ、ふぐは不具に通じるから、ふくで福です。福の神。ダエモン君はBSD系の守護神。
そりゃ、 ダエモン君からのソース読めって言うメッセージだな。
sys/dev の中にあるデバドラを見るしかないでしょう。しかもiwn0ってんだから、ピンポイントです。/usr/src/sys/dev/if_iwn.c
iwn_init(struct ifnet *ifp) { : /* Check that the radio is not disabled by hardware switch. */ if (!(IWN_READ(sc, IWN_GP_CNTRL) & IWN_GP_CNTRL_RFKILL)) { printf("%s: radio is disabled by hardware switch\n", sc->sc_dev.dv_xname); error = EPERM; /* :-) */ goto fail; }
リナでOFFの時に採取しておいたdmesgを覗いてみるか。リナのそれは、無駄に長いんだよな。BSD系みたいに簡潔にいかないものかね。ぶつくさ言いながら探してみる。
[ 10.794876] thinkpad_acpi: Lenovo ThinkPad SL510, model 2875CTO [ 10.797502] thinkpad_acpi: radio switch found; radios are disabled
850行もあるメッセージから、こういうのを探すのは至難の技ですよ。しかも不親切なメッセージ。これだから、、、、(以下自粛)
この奇怪な現象、上に挙げた説の他にも色々考えられるぞ。例えば、時空の歪みにより、別のパラレルワールドへ遷移した。とか、CIA若しくはハム(ああ、これ公安部ってのの警察の隠語ね)の連中が、オイラーをリクルートするための最終試験とかね。
留守の間に潜入して、パソコンのスイッチをちょいと操作する。それを被試験者(オイラーの事ね)が、いかにして発見修復するか? その時間と過程を観察する。
どうでっしゃろ? オイラーの問題解決能力は?
付け加えておくと、OpenBSDでも一目瞭然の他dmesgを確認してます。最後の行に、ちょろと、スイッチがOFFになってますよーってのが出てた事を確認済です。なんの迷いもなく、最後の数行を見るって事だけで、用が足りてますよ。
と、自己アッピールしておこう。
前回の疑問
shで 鍵カッコを使ったテスト句の、閉じカッコ問題。
ob64$ touch foo ob64$ [ -s foo ]; echo $? 1 ob64$ [ -s foo ; echo $? ksh: [: missing ] 2
どうも、kshが番人になってるっぽい。バランスを見てるの?
この鍵カッコ、某Lisp方面では、スーパー・カッコと呼ばれる事がある。S式を閉じるコッカが連続すると、コッカが足りなかったり、余分に入力しちゃう事がある。そんな場合、このスーパー・カッコを使うと、程よくバランスしてくれる。
人気が出て常用されるかと思ったら、余り見かけない。頑張って使ってるのは、clojureぐらいなものかな。
ksh 探検
また、gdbにかけられるように整えてみる。
ob64$ pwd /usr/src/bin ob64$ cp -R ksh /tmp
/tmpの下はRAM-DISKなんで、SSDの寿命を縮めないし、ユーザー権限でコンパイル出来るから、常用の作業場所だ。
ob64$ make -j4 : cc -O2 -pipe -DEMACS -DVI -I. -I/tmp/ksh -I/tmp/ksh/../../lib/libc/gen -Wall -Wpointer-arith -Wuninitialized -Wstrict-prototypes -Wmissing-prototypes -Wunused -Wsign-compare -Wshadow -MD -MP -c misc.c misc.c:16:10: fatal error: 'charclass.h' file not found #include "charclass.h" ^~~~~~~~~~~~~ 1 error generated. *** Error 1 in /tmp/ksh (<sys.mk>:87 'misc.o')
何と、ヘッダーファイルが見つからんエラー。正規の場所から移動しちゃったので、相対参照してるヘッダーファイルが見つからんのだな。よくある事。
CFLAGS=-ggdb3 -O0 CFLAGS+=${DEFS} -I. -I${.CURDIR} -I${.CURDIR}/../../lib/libc/gen
Makefileを開いて、ヘッダーファイルの在処を確認。場所が分かったら、コピーしてきちゃえ。 ついでに、-O2 -pipeを(デフォで)指定してる、CFLAGSを、gdbに適合するように書き換え。 これで、make clean してから、再コンパイル。
取り合えずemacs上のgdbからkshを起動して、どこらで待ちになってるか確認。
(gdb) r Starting program: /tmp/ksh/ksh C-c C-c Program received signal SIGINT, Interrupt. _thread_sys_read () at -:3 3 -: No such file or directory. (gdb) bt #0 _thread_sys_read () at -:3 #1 0x012793a2 in _libc_read_cancel (fd=0, buf=0xcf7d7ac3, nbytes=1) at /usr/src/lib/libc/sys/w_read.c:27 #2 0x16484bec in blocking_read (fd=0, buf=0xcf7d7ac3 "\026J<\344lH", nbytes=1) at misc.c:1081 #3 0x1645653b in x_getc () at edit.c:124 #4 0x164606ea in x_e_getc () at emacs.c:1837 #5 0x16459c8e in x_e_getu8 (buf=0xcf7d7b93 "", off=0) at emacs.c:1847 #6 0x16459554 in x_emacs (buf=0x55081008 "", len=4096) at emacs.c:321 #7 0x16456131 in x_read (buf=0x55081008 "", len=4096) at edit.c:102 #8 0x1647e78c in getsc_line (s=0x51632388) at lex.c:1099 #9 0x1647e314 in getsc__ () at lex.c:988 #10 0x1647b8bb in getsc_bn () at lex.c:1633 #11 0x16477941 in yylex (cf=44) at lex.c:167 #12 0x1648a174 in get_command (cf=0) at syn.c:206 #13 0x16489e25 in pipeline (cf=0) at syn.c:77 #14 0x16489bbe in andor () at syn.c:98 #15 0x16489643 in c_list (multi=0) at syn.c:118 #16 0x16489543 in yyparse () at syn.c:64 #17 0x164894e6 in compile (s=0x51632388) at syn.c:767 #18 0x16481232 in shell (s=0x51632388, toplevel=1) at main.c:604 #19 0x164808dd in main (argc=1, argv=0xcf7d9934) at main.c:431
フレームを上ったり下りたりしながら、ソースをつまみ読みするのが乙。 syn.cあたりが核になりそうな予感がするぞ。
今日の所はここまで。gdbから扱えるようにしたkshを含む一式は、tarで固めてsaveしておこう。 再開する時は、/tmpに展開するだけ。
騒ぎを起こせ
ob64$ cat z.sh [ -s NOTES ; echo $?
バランスの取れていないスクリプトを喰わせて、エラーを起こそう。エラーを報告してる場所は、下記の様に3か所だったので、そこにBPを貼っておく。
ob64$ cd /usr/src/bin/ksh ob64$ fgrep missing *.[ch] : ;; 色々出てくるので、絞りこみした ob64$ fgrep 'missing ]' *.[ch] c_test.c: bi_errorf("missing ]"); expr.c: evalerr(es, ET_STR, "missing ]"); lex.c: yyerror("missing ]\n");
(gdb) b bi_errorf Breakpoint 1 at 0x272fd: file io.c, line 65. (gdb) b evalerr Breakpoint 2 at 0x2355e: file expr.c, line 227. (gdb) b yyerror Breakpoint 3 at 0x3238f: file lex.c, line 934. (gdb) r z.sh Starting program: /tmp/ksh/ksh z.sh Breakpoint 1, bi_errorf (fmt=0x370c7bc0 "missing ]") at io.c:65 65 shl_stdout_ok = 0; /* debugging: note that stdout not valid */
早速、引っかかってくれた。して、そこに至る道は?
(gdb) bt #0 bi_errorf (fmt=0x370c7bc0 "missing ]") at io.c:65 #1 0x170cd64d in c_test (wp=0x6d2614ec) at c_test.c:121 #2 0x170e49da in call_builtin (tp=0x5f38de48, wp=0x6d2614ec) at exec.c:1032 #3 0x170e35c2 in comexec (t=0x4aa97d88, tp=0x5f38de48, ap=0x6d2614ec, flags=0, xerrok=0xcf7cd484) at exec.c:541 #4 0x170e0a5d in execute (t=0x4aa97d88, flags=0, xerrok=0xcf7cd484) at exec.c:130 #5 0x170e0d20 in execute (t=0x5f38d688, flags=0, xerrok=0xcf7cd674) at exec.c:166 #6 0x170fb36f in shell (s=0x61a37188, toplevel=1) at main.c:626 #7 0x170fa8dd in main (argc=2, argv=0xcf7cdaa4) at main.c:431
frame #1 の該当箇所。c_test関数の中
if (strcmp(wp[0], "[") == 0) { if (strcmp(wp[--argc], "]") != 0) { => bi_errorf("missing ]"); return T_ERR_EXIT; }
少しwpを確認
(gdb) p argc $2 = 2 (gdb) p wp[1] $3 = 0x4a422e78 "-s" (gdb) p wp[2] $4 = 0x71d13c48 "NOTES"
今まで鍵カッコ [ は、/bin/[ を呼んでいると思ってたけど、c_test.c にそっくりそのまま取り込んで、shの内部コマンドになってるよ。
shellのbuilt-inにしておけば、外部コマンドを呼び出すオーバーヘッドを大幅に低減出来て、スクリプトの実行スピード向上に寄与出来るとな。
kshが返すエラーコードは
#define T_ERR_EXIT 2 /* POSIX says > 1 for errors */
ここに定義されてた。
ちなみに、テストで使った演算子、-s は、test_eval関数の中で
case TO_FILGZ: /* -s */ return stat(opnd1, &b1) == 0 && b1.st_size > 0L;
こんな風に定義されてた。
from sh/USD.doc
S. R. Bourne さんは、An Introduction to the UNIX Shell の中で、
The test command is known as `[' and may be invoked as such. For aesthetic reasons, the command ignores a close bracket `]' given at the end of a command so [ -f filename ] and test -f filename are completely equivalent. Typically, the bracket notation is used when test is invoked inside shell control con- structs.
こんな事を書いておられる。閉じ括弧は、美学だそうです。
この資料、NetBSDのshに付属していた(shをコンパイルするsh.txtが作成される)。
exec /bin/[
そんじゃ、内蔵コマンドじゃなくて、外部コマンドはどうよ?
ob64$ cat z.sh /bin/[ -s z.sh
こんな外部コマンドを使ったやつを実行。BPを何処に置くかは試行錯誤で見つけておいた。
(gdb) bt #0 exchild (t=0xcf7eb6c0, flags=0, xerrok=0xcf7eb8c4, close_fd=-1) at jobs.c:447 #1 0x16c74ecb in comexec (t=0x427657c8, tp=0x36c60288 <findcom.temp>, ap=0x42d1aa2c, flags=0, xerrok=0xcf7eb8c4) at exec.c:692 #2 0x16c71a5d in execute (t=0x427657c8, flags=0, xerrok=0xcf7eb8c4) at exec.c:130 #3 0x16c8c36f in shell (s=0x47175508, toplevel=1) at main.c:626 #4 0x16c8b8dd in main (argc=2, argv=0xcf7ebcf4) at main.c:431
(gdb) 441 while ((i = fork()) < 0 && errno == EAGAIN && forksleep < 32) { (gdb) [New process 69351] [: missing ] (gdb) c Continuing. [Inferior 1 (process 69351) exited with code 02]
forkで新らしいプロセスが生れて、エラーで終了。そこで、やおら /bin/[ のソースを参照してみる。
int main(int argc, char *argv[]) { extern char *__progname; int res; : if (strcmp(__progname, "[") == 0) { if (strcmp(argv[--argc], "]")) errx(2, "missing ]"); argv[argc] = NULL; }
内蔵のtestコマンドと同様なエラー検出を行なっていた。
glob
ls *.c みたいなワイルドカードによる、ファイル展開がどう行われているか、調べてみるか。 (リクエストが有ったので)こういう展開は一般的に、globなんて名前で呼ばれているんで、定義してる関数が無いか探してみた。どんピシャな奴が存在してた。
ob64$ cat z.sh ls ????
お題は、こんなの。* を期待してた人、御免。今回は趣向を変えて、ピッタリと4文字の名前のファイルをリストアップしようと言う魂胆です。
(gdb) b glob Breakpoint 1 at 0x1b28e: file eval.c, line 956. (gdb) r z.sh Starting program: /tmp/ksh/ksh z.sh Breakpoint 1, glob (cp=0x6b0f7228 "\a?\a?\a?\a?", wp=0xcf7d1020, markdirs=0) at eval.c:956 956 int oldsize = XPsize(*wp); (gdb) bt #0 glob (cp=0x6b0f7228 "\a?\a?\a?\a?", wp=0xcf7d1020, markdirs=0) at eval.c:956 #1 0x18ab7cf5 in expand (cp=0x6b0f7e08 "\001?\001?\001?\001?", wp=0xcf7d1020, f=75) at eval.c:571 #2 0x18ab6457 in eval (ap=0x441011d0, f=11) at eval.c:93 #3 0x18abb5f7 in execute (t=0x570e7a88, flags=0, xerrok=0xcf7d1214) at exec.c:89 #4 0x18ad636f in shell (s=0x65ff3708, toplevel=1) at main.c:626 #5 0x18ad58dd in main (argc=2, argv=0xcf7d1644) at main.c:431
継続すると
(gdb) c Continuing. Breakpoint 2, exchild (t=0xcf7d7d90, flags=0, xerrok=0xcf7d7f94, close_fd=-1) at jobs.c:390 390 int rv = 0; (gdb) c Continuing. [New process 63223] io.c sh.1 sh.h vi.c z.sh [Inferior 1 (process 63223) exited normally]
glob関数は、eval.cと言う、いかにもって場所に置いてある。ソースを覗くと
/* * glob * Name derived from V6's /etc/glob, the program that expanded filenames. */ /* XXX cp not const 'cause slashes are temporarily replaced with nulls... */ static void glob(char *cp, XPtrV *wp, int markdirs) :
こんな事がコメントに書いてあった。unix v6の時代にはファイル名の拡張に/etc/globってのを使っていたとな。深入りすると大変な事になりそう。
ちょいと趣を変えて、 glob(3)
EXAMPLES A rough equivalent of `ls -l *.c *.h' can be obtained with the following code: glob_t g; g.gl_offs = 2; glob("*.c", GLOB_DOOFFS, NULL, &g); glob("*.h", GLOB_DOOFFS | GLOB_APPEND, NULL, &g); g.gl_pathv[0] = "ls"; g.gl_pathv[1] = "-l"; execvp("ls", g.gl_pathv);
無理にC語に拘らなくても、 /usr/local/lib/python2.7/glob.py こういうのがあるな。
ob64$ /usr/bin/env python Python 2.7.15 (default, Oct 11 2018, 16:48:40) [GCC 4.2.1 Compatible OpenBSD Clang 6.0.0 (tags/RELEASE_600/final)] on openbsd6 Type "help", "copyright", "credits" or "license" for more information. >>> import glob >>> glob.glob('????') ['io.c', 'sh.1', 'sh.h', 'vi.c', 'z.sh'] >>> glob.glob('*.h') ['c_test.h', 'config.h', 'edit.h', 'expand.h', 'lex.h', 'sh.h', 'shf.h', 'table.h', 'tree.h', 'tty.h', 'charclass.h']
ちょいと、シェバングで見掛ける方法でpythonを起動してみた(他意は無い)。
中身はどうなっている?
これからやってみるか。まて、余白が無いので、、、 と 、フェルマーさんみたいな事を言ってみる。
a^n + b^n = c^n
3以上の自然数nについて、上式を満たす自然数の組 (a, b, c) は存在しない。フェルマーの最終定理の話ね。
割と最近になってワイルズさんが、証明した。
rksh
余白が無いとか言っておきながら、kshのMakefileを見てたら
LINKS= ${BINDIR}/ksh ${BINDIR}/rksh LINKS+= ${BINDIR}/ksh ${BINDIR}/sh
shは理解できるけど、rkshって初めてお目にかなるな。
-r Restricted shell. A shell is "restricted" if this option is used; if the basename the shell was invoked with was "rksh"; or if the SHELL parameter is set to "rksh". The following restrictions come into effect after the shell processes any profile and ENV files: o The cd command is disabled. o The SHELL, ENV, and PATH parameters cannot be changed. o Command names can't be specified with absolute or relative paths. o The -p option of the built-in command command can't be used. o Redirections that create files can't be used (i.e. `>', `>|', `>>', `<>').
ob64$ rksh ob64$ pwd /tmp/ksh ob64$ cd ~ rksh: cd: restricted shell - can't cd
一番分かり易いのが、上の例だな。某会社の会長だった人みたいに、出国禁止という制限を課されるとな。PATHも変更禁止のみならず。勝手なコマンドも指定できない。
こういう環境から抜け出るウルトラCな技はあるのかな? チャレンジして脱獄のプロになろう。
text to pdf
プレーンなテキストファイルをPDFに変換するには、LibreOffice / OpenOffice に付属してるコマンドを使うらしい。
unoconv -f pdf hoge.txt
これで、hoge.pdf が出来上がる。日本語は豆腐になるのは、ご愛嬌。笑って許せ!
C-u M-x ps-print-buffer して hoge.txt.ps を作り、それを ps2pdf にかける。これも豆腐になる。
黙ってM$の軍門に下り、PDFに出力するプリンターを使っとけ。