Logo address

Mac Life (その2)

2024-11-14
2024-11-27 追加 (macFUSE)

はじめに

愛用していた MacBook Pro (以下 mbook) が限界に達したのでついに買い替えた。
この mbook は Late 2012 の製品で、現在 macOS Sierra 10.12.8 の下で動いている。
もう OS はアップデートできない。またそれに伴ってアプリもアップデートできない。
Google にも、愛想を尽かされたか、Chrome のアップデートできなくなった。
さらにメールは受信できても送信できなくなった。
iCloud のメール送信サーバに繋がらないのである。(受信できるだけに、これは焦る)
幸い、最近手に入れた iPad があったので(これは息子からのプレゼントである)、メールを出す方は何とかこれで凌いでいた。しかし iPad は(仕事用にはできていないので)大変使いにくい。
そこでとうとう買い替えることにしたのである。
買ったのは MacBook Air(13インチ M3 2024)である。(以下 MBair)
MacBook Pro には買い替えなかった。もう Pro を使うような生活はしていないからである。

以下は mbook から MBair に引っ越しするまでの奮戦記である。引っ越したあとに色々と問題が露呈するかも知れないが、それらは気がついたときに気ままに追記することとする。

移行アシスタンス

Mac には「移行アシスタンス」が備わっている。古い Mac から新しい Mac に簡単に移行できるツールを提供することは、Apple にとって重要なサービスである。

新しい Mac を立ち上げると、いきなり移行アシスタンスが動いた。これは改善した方が良い。その前に、準備するものを説明しておくべきだ。
僕の場合には、古い Mac に大量のデータを抱えている。この場合、手順としては古いデータをハードディスクに移して、それを新しい Mac に取り込んだ方が早いはずである。Mac には TimeMachine が備わっており、移行に TimeMachine が使えるようになっているのだ。古い Mac のデータは TimeMachine にあると推測するのが当然だ。
しかしいきなり WiFi 環境を使ってのデータ転送モードに入ってしまった。これは時間がかかる。しようがないから部分的に有線にして、ようやく一晩でデータ転送を完了した。(僕は膨大なデータを持っている)

新しい Mac に作成(あるいは移行)されたデータは次の3つに分類できる:

  1. Apple 提供の OS 関係データとアプリ (macOS Sequoia 15.0.1 となっていた)
  2. 僕がインストールして使っていた第3者提供のプログラム
  3. 僕のオリジナルなプログラムやデータ
特に新しい Mac は CPU の変更があったので、プログラム関係は全面的に入れ替える必要がある。
それでも、さすがに Apple 提供のデータはしっかり移行してくれる。第3者提供のデータは有名どころは移行してくれるが、そうでないもの、例えば Plan9 関係までは面倒を見てくれない。僕のオリジナルなプログラムは当然ダメ。

最初はメールが動かなかった。問題は古いメールデータにあった。$HOME/Library/Mail には古いデータが含まれており、
"Mail" の名称を変更して "Mail-" として潰すことで解決した*Mail.app は新たに "Mail" を作り直してくれる。僕の Mac のメールのデータは(Apple の標準設定に従って)ネット上の IMAP で管理されていたので、古いメールのデータは今までとおりにアクセスできる。なお、ファイルを消すのは慎重であるべきだ。本当に要らない確信できるまで残すのが無難である。

Python や Lua は提供元から直接ダウンロードした方が遥かに早い。ついでに nim をインストールしておいた。時間があればトライしてみよう。
Lua のソースコードは非常に小さいのでソースをダウンロードしてコンパイルはあっという間に終わる。もっとも前もって Xcode のインストールが必要ではあるが...

第3者提供のデータのうち、Homebrew で管理されていたものは、Homebrew を使えば原理的には復元するのだが、どうしたわけか Homebrew がまともに動かない。仕方がないので Macport を使った。特に rlwrap は Macport のおかげで助かった。しかし時間がかかること... どうして?

今回、移行を困難にしている要素は2つある。
(A) CPU が x86 から arm に変わった
(B) セキュリティモデルが変化している

(A)の問題は Apple にとっては大した問題では無いはずである。これまで Apple はそうしたことは経験済みだから。問題は(B)にある。

セキュリティは現代の情報社会において深刻な問題となっている。


* メーリングリストなどのメールボックスの情報が失われていた。多分、もう一度作り直しか?
今は元通りに動いている。作り直したわけでは無い。ジタバタしたわけでもない。何となく、である。

ネット関係

サービス

ネットサービスの状態を見る

netstat

open port を表示しない。困る
--> 解決 (以下 2024-12-01 に追加)
netstat で tcp の open port を表示させるには
	netstat -anp tcp
でよい。元記事はref[1]である。

動作を確認するには open port を自分で作成してみればよい。僕の環境では Plan9port がインストールされている。その場合 listen1 が使える。

	listen1 'tcp!*!8081' echo Hello
を実行すれば tcp の 8081 ポートを開けて、そこにアクセスが来た場合には echo Hello が実行される。listen1 は相手を選ぶことができて、"*" は相手は誰でもよいとしている。
実際、僕の mbook から
	nc mbair 8081
でアクセスすると "Hello" が表示される。つまり原理的には 8081 ポートを通じて悪質なプログラムを仕込むことも可能なのである。

この時 netstat は開きポート 8081 を捉えているか? YES.
mbair で

	netstat -anp tcp
を実行してみれば、多くの行の中に
	tcp46      0      0  *.8081                 *.*                    LISTEN
と表示されているはずである。

他にポート 50007000 が気になるが、7000 は Apple のファイルシステムに関するもの、5000 に関しては記事[2]にある。

セキュリティの基本は戸締りである。戸締りができていることが見えるようにしておかないと、システムが外部から操られることになりかねない。

[1] How can I list my open network ports with netstat?
https://apple.stackexchange.com/questions/117644/how-can-i-list-my-open-network-ports-with-netstat

[2] Why always something is running at port 5000 on my mac
https://stackoverflow.com/questions/72369320/why-always-something-is-running-at-port-5000-on-my-mac

セキュリティ

注意: この小節の内容は netstat の記事で訂正されます。ポートスキャンしなくても判る。

セキュリティ、ネット診断
nmap.app があるらしい
しかし Apple silicon には対応していないらしい。

余計なサービスをしていないか?
indetd, xinetd を使っていたときにはわかった。しかし今は
ポートスキャンをしないとわからない --> 訂正: netstatで判る
ポートスキャンしなくても判るようにすべきだ
Mac には
 システム設定 → ネットワーク → ファイアウォール → ON/OFF
があるが、荒っぽすぎる。例えば僕の場合 ssh アクセスアクセスを許しているが、外部から実際にアクセスしないとこのことが判らない。

Plan9 の上には自作のポートスキャナーがある。使い方は

	usage: portscan [-r range] host
range でポートの範囲を指定する。これを指定しないとポートは 1:1023 までとなる。このツールは実際にアクセスして開いているか否かを調べる。
以下は Plan9 ホスト maia での mbair へのアクセス実験である。
maia% portscan mbair
trying tcp!mbair!1
dial failed: connection refused
...
trying tcp!mbair!21
dial failed: connection refused
trying tcp!mbair!22
connected to tcp!mbair!22 on /net/tcp/24
tcp!192.168.0.3!46869 -> tcp!192.168.0.19!22
trying tcp!mbair!23
dial failed: connection refused
...
trying tcp!mbair!1023
dial failed: connection refused
これを見る限り ssh ポート(22ポート)は開いていたが、残りは閉ざされていることが判る。
ssh ポートは僕が開けていたはずである。「残りは閉ざされている」と書いたが 1024 以上は調べられていない。
ボートは 1 から 65536 まである。これらの全てをスキャナで調べることは負荷が大きい。
もしも悪意のプログラムが(知らないうちにインストールされ)実行されていると、1024 以上、しかも非常に大きな番号のポートを使うであろう。だからポートスキャナに頼らない方法で安全性を確認できるようにするべきである。それは可能なはずであるから。(Pan9 はそのようになっている)

Apple は inetd や xinetd の代わりに plist でサーピス管理をしている。例えば ssh は
/System/Library/LaunchDaemons/ssh.plist で冠されていた。それによると

	Disabled YES
となっているのであるが、現実には ssh からアクセスできるのだよね。この意味は何だ?

この問題に関係したネット上の情報がある[1]。
これはよく解らんという話。しかし man launchd.plist(5) に次の記述がある:

Disabled <boolean>
This optional key specifies whether the job should be loaded by default. Note that this key may be overridden through the enable subcommand of launchctl(3).
Previous Darwin operating systems would modify the configuration file's value for this key, but now this state is kept externally.
この説明も解るような解らないような...

Launchd

man launchd(8)

このマニュアルによると plist の置き場所は次の4箇所である。役割も書かれている。

     ~/Library/LaunchAgents         Per-user agents provided by the user.
     /Library/LaunchAgents          Per-user agents provided by the administrator.
     /Library/LaunchDaemons         System-wide daemons provided by the administrator.
     /System/Library/LaunchAgents   Per-user agents provided by Apple.
     /System/Library/LaunchDaemons  System-wide daemons provided by Apple.

[1] /usr/libexec/sshd-keygen-wrapper
https://discussions.apple.com/thread/2635087?sortBy=rank

Launchctl

まだ解らぬことが多い。主なマニュアルは
	launchctl(1)
	launchd.plist(5)

以下では

	MacBook-Air$
は MacBook air のコマンドプロンプト。

launchd/launchctl disabled key and list option

MacBook-Air$ launchctl list|grep ssh
65470	0	com.openssh.ssh-agent
MacBook-Air$
これがどこにあるのか、判る仕組みがあって当然。この場合
MacBook-Air$ ls /System/Library/LaunchAgents|grep ssh
com.openssh.ssh-agent.plist
open /System/Library/LaunchAgents/com.openssh.ssh-agent.plist
MacBook-Air$
5通りを試せば判るので、我慢するかコマンドを作りなさいと言うことか?

これは ssh 用(client用)(多分)

Sshfs

事実上使えなくなった。困る人が多いのではないだろうか?
sshfs が使えるためには macFUSE のインストールが必要。

Apple が sshfs を嫌う理由は、Apple の大嫌いなカーネルの拡張(FUSE)が要求されるからであろう。下手するとセキュリティ上の大問題が発生する。しかし FUSE の技術は大変役に立つので、Apple が直々に関与したら如何。

macFUSE

2024/11/27

遂にインストールに成功
macFUSE は普通の人にとってはなくてもよいものである。しかし、僕のように色々なOSの混在環境で作業する者には是非とも欲しい。例えば sshfs を使いたい場合である。
インストールは危険である。十分に OS の役割を熟知している必要がある。
以下にインストールの要点のみを解説する。

macFUSE は本家
https://osxfuse.github.io/
から直接入手する。
僕のは macFUSE 4.83 で Apple Silicon に対応していた。
インストール途中に

The macFUSE system extension could not be loaded.

Please open the Privacy & Security System Settings and allow loading system software from developer "Benjamin Fleischer". A restart is required before the system extension can be loaded.

For detailed instructions see Getting Started.

と言われる。対処の方法は
https://github.com/macfuse/macfuse/wiki
に書かれている。見るべし。
電源ボタンを押しながらブートして、「起動セキュリティユーティリティ」を使ってセキュリティ設定を変更せよと書かれている。しかし、これがどこにあるのか、見つけるのに時間がかかった。ようやく見つけた。(下図)

設定の方法は文献[1]に書かれている
設定のポイント:

注: リモート管理は許可する必要はない

[1] Appleシリコンを搭載したMacの起動ディスクのセキュリティ設定を変更する
https://support.apple.com/ja-jp/guide/mac-help/mchl768f7291/mac

USB disk のリモートアクセス

Mac Book の USB ポートに挿入されたディスクは、ネット越しにアクセスするユーザからはどのように見えるのだろうか? ネット越しに USB disk のアクセスを許すなんてことは、僕には理解ができない。こんな機能があって喜ぶのは不正侵入者ぐらいでしょうね?

実験をしてみた。

まずは普通の USB disk。このFinder の示すこのディスクの情報はFAT32。共有とアクセス権:
「カスタムアクセス権が割り当てられています」と表示されている。このディスクは ssh でアクセスしている もう一つの MacBook(mbair) から隠されていない。

mbair$ ls /V*
FAT32	root
mbair$ ls /V*/FAT32
9front-10277.amd64.iso.gz	inst2.txt			memo4				setup
9front-9931.386.iso		memo				memo5				sys0.txt
9front.iso			memo2.txt			memo6				sys1.txt
inst.txt			memo3.txt			private
mbair$ ls -l /V*/FAT32
total 2664168
...
-rwxrwxrwx  1 arisawa  staff       3719  5  4  2024 inst.txt
-rwxrwxrwx  1 arisawa  staff      12787  5  4  2024 inst2.txt
-rwxrwxrwx  1 arisawa  staff       1898  4 14  2024 memo
...
mbair$
そして、ファイルの内容を自由に見ることができる。また書き込みもOK.

今度は同じことを新しい MacBook(mbair) に対して行う。
mbair のFinder の示すこのディスクの情報は同様に FAT32。共有とアクセス権:
「カスタムアクセス権が割り当てられています」と表示されている。
当然、mbair で見る限り同じ内容が表示されるが、ssh でアクセスしている mbook からは USB disk は隠されていてアクセスできない。常識的なセキュリティの下に動く様になったのである。

Plan9 関係

Plan9port

Plan9 のプログラムは、高度なことをシンプルな美しさで実現している。天才たちの芸術作品とも言える。これらを Unix 環境に移植するプロジェクトが Russ Cox により始められた。もちろん全ての移植は不可能であるが、しかしかなりのものが移植されている。僕にとっては特にありがたいのは rc (シェルスクリプト)である。rc はシンプルにして強力である。

https://9fans.github.io/plan9port
Plan9port (by Russ)
容易にコンパイルできる。
しかし factotum を実行すると

	Segmentation fault
となる。Mount で問題を起こすのではないかと思う。これも FUSE 問題。

Drawterm

ここでは 9front の drawterm を扱う。
9front の drawterm の流れの中で Jxy による Mac の Metal 対応のものが出ている。これは今は
https://github.com/Plan9-Archive/drawterm-metal-cocoa
に置かれている。(Archive に置かれているのは、以下の「問題点」ゆえか?)

drawterm のコードを眺めていると Ken Thompson のコードが多数流用されていることが判る。

	Ken Thompson → Rob Pike → Russ → Cinap → Jxy
の流れが見える。(もちろん他にも多くの人が関与している)

コードの中に次のコメントが含まれている。

	kern/error.c:43: char Egreg[] = "ken has left the building";
コメントの中の "ken" とは Ken Thompson 以外には考えにくい。
Drawterm は unix ホストから Plan9 ホストにアクセスするためのグラフィックス端末であり、マウスを使って編集できる。Drawterm の内部は小さな Plan9 として設計されている。コードのディレクトリ kern 以下のファイルがそれである。従って Plan9 端末を使って Plan9 ホストにアクセスするのと同じ考え方であり、類似のコードが流用できる。

Jxy の Drawterm は Mac の Metal に対応している。新しい Mac (mbair) ではすんなりとコンパイルでき、現在は気持ちよく動いている。Jxy はなかなか頑張っている。Drawterm が Mac の日本語入力に対応いているのだ。この機能は重宝する。

僕は Plan9 に関係した自作のプログラムを多数持っている。
そもそも、Web のサーバーも自作。このサーバーは Plan9 の下で動いている。また、この中で動いている多数のプログラムも全て自作である。
Mac からは Drawterm
Metal 版

問題点

(a) font
表示される文字が小さすぎる
(b) full screen の下での画面切り替えで落ちる

(a) の問題は $home/lib/profile

	font=/lib/font/bit/lucida/unicode.14.font
を追加して解決した。ただし完全解決ではない。最初のパスワードの入力画面の文字サイズは小さいままである。パスワードが通過してしまえば問題はないので我慢している。

(b) の full screen モードは未解決。

Apple が目指しているもの

新しい MacBook Air から見える Apple の目指している MacBook の将来像は次の様なものであろう。

誰もが使える安心・安全なデバイス

これは Apple の昔からの目標であるが、難しい目標ゆえに道半ばである。
問題はこの目標が、利用の自由を奪うことにある。
利用範囲を限定し、実行されるプログラムが全て Apple 製であれば可能であろう。iPhone や iPad は Apple の統制が効くので、危険なプログラムを排除するのは難しくはない。これらのデバイスの利用者は限定された使い方で満足するだろう。
そして Apple はその方向へ進んでいるのではなかろうかと思える。
それが良い利用者も多いであろうが、僕の場合は(自由が望めないなら) Mac から離れることになる。また Apple 製品の利用者からは Apple 製品を支える良質な技術者は生まれないであろう。人は失敗を繰り返しながら成長するものだから。

Apple 流の(初心者に優しい)システム設計では

  1. ソフトウェアが巨大化する傾向にある。
  2. ソフトウェアのシンプルさが失われていく
  3. バグの原因を作る
これらは Apple に課せられた試練である。

Apple によるコード署名

署名の確認法

文献[6]が役に立った。

MacBook-Air$ codesign -vd /Applications/Xcode.app
Executable=/Applications/Xcode.app/Contents/MacOS/Xcode
Identifier=com.apple.dt.Xcode
Format=app bundle with Mach-O universal (x86_64 arm64)
CodeDirectory v=20400 size=790 flags=0x2000(library-validation) hashes=14+7 location=embedded
Signature size=4797
Info.plist entries=43
TeamIdentifier=59GAB85EFG
Sealed Resources version=2 rules=13 files=108454
Internal requirements count=1 size=220
MacBook-Air$

自作の場合も署名は入っている。実験してみよう。この署名を削除するとどうなるか?
以下で c1.c はCで書かれた適当な自作のプロクラム。

MacBook-Air$ cc c1.c
MacBook-Air$ ls -l a.out
-rwxr-xr-x  1 arisawa  staff  33656 11  2 07:31 a.out
MacBook-Air$ codesign -vd a.out
Executable=/Users/arisawa/test/filename/a.out
Identifier=a.out
Format=Mach-O thin (arm64)
CodeDirectory v=20400 size=382 flags=0x20002(adhoc,linker-signed) hashes=9+0 location=embedded
Signature=adhoc
Info.plist=not bound
TeamIdentifier=not set
Sealed Resources=none
Internal requirements=none
ちゃんと署名が入っている。この署名を削除すると、もちろん実行はできない。
MacBook-Air$ codesign --remove-signature a.out
MacBook-Air$ ls -l a.out
-rwxr-xr-x  1 arisawa  staff  33248 11  2 07:34 a.out
MacBook-Air$ codesign -vd a.out
a.out: code object is not signed at all
MacBook-Air$ a.out
Killed: 9
MacBook-Air$
プログラムサイズが小さくなっている。署名部分は a.out の末尾に置かれていたに違いない。file コマンドなどの既存のツールと両立させるために。

署名の仕組みによって、怪しいコードは排除される。もちろんスクリプト言語に対しては、この方法には限界があるだろう。

マニュアル CODESIGN(1) を読むに、署名も codesign を使うらしい。

参考文献

[1] iOSおよびiPadOSのアプリコード署名プロセス
https://support.apple.com/ja-jp/guide/security/sec7c917bf14/web

[2] macOSでのアプリのコード署名プロセス
https://support.apple.com/ja-jp/guide/security/sec3ad8e6e53/web

[3] TN3161: Inside Code Signing: Certificates
https://developer.apple.com/documentation/technotes/tn3161-inside-code-signing-certificates/

[4] 電子署名によるアプリの依存関係の検証
https://developer.apple.com/jp/videos/play/wwdc2023/10061/

[5] Gatekeeperを利用するためのアプリへの署名
https://developer.apple.com/jp/developer-id/

[6] macOSでアプリケーションが署名されてるかどうか確認する
https://hokaccha.hatenablog.com/entry/2016/12/26/231436

付録

Mac のおせっかい

なんだか僕がキーを打ち込んだことになっているのだよね。気が付くと変な文章が勝手に打ち込まれて、僕の文章を汚しているのだ。「Mac のおせっかい」でネットを調べると、評判が悪いわ。こんなのは絶対に許せない。対処の仕方が記事[3]にある。Apple は何を考えているのだろうね...

今日は寒かった。そして思わず「寒い、寒い」と呟いた。それで問題が解決した。テキストにも「寒い、寒い」が打ち込まれているのだね。対処は文献[4]にある。

[3] 入力途中の単語を自動入力する
https://support.apple.com/ja-jp/guide/mac-help/mchlp2299/mac

[4] Macでメッセージや書類を音声入力する
https://support.apple.com/ja-jp/guide/mac-help/mh40584/mac

Mail.app

古い MacBook のユーザーは驚くね。メールの探し方が解らない! と思ったら、小さく「探索」の文字が見える。僕は新しい環境での Mail.app の使い方で解らないところがいっぱいある。

図2: mbook の mail.pp のタブ欄

図3: mbair の mail.pp のタブ欄

空白を含むファイル名

Apple は空白を含むファイル名が大好きである。
しかし僕の様なコマンド派は、そのようなファイル名を嫌う。煩わしいだけだから。
unix のファイル名規則は極めて簡単で、パス名はC言語の文字列(もちろん長さ 0 ではない)であれば何でも許される。文字コード 0 は文字列の終端であるから、パス名の中には含まれない。ファイル名規則では "/" は許されない。これはディレクトリの区切り記号であるから。しかしこの規則は広すぎるのである。この規則に従えばファイル名の一覧を人が読めるように正しく表すことは不可能である。特に改行記号をファイル名の中に許していることが問題である。これは unix のファイル名規則の病的な側面である。

改行記号は酷すぎるとしても、空白だけであればシェルのシンタックスに変更を加えれば何とかなる。もちろんそのままだとシェルは間違えるので手を打たなくてはならない。現在 Bash では "\ " で空白を表している。Plan9 のシエル rc はまた別の方法を採っているが、僕はどちらも嫌いである。幸せに暮らせるためには、病的なファイル(空白を含むファイル名も)全て追放したい。
次の Python のプログラム a1.py は病的なファイルを炙り出す。炙り出すだけであって、消すなり名前を変えて残すなり、対処はユーザに任せている。

#!/usr/bin/env python3
# Goal: replace all control characters and spaces in a string to "?"
#
usage = "usage: namechk [dir dir ...]"

import sys
import os

def fix(s):
  code = ""
  for i in range(0,len(s)):
    if 0 <= ord(s[i]) <= 0x20 or ord(s[i]) == 0x7f:
      code = code + " "+format(ord(s[i]),"x")
      s = s[:i]+"?"+s[i+1:]
  if len(code) > 0:
    return s + '	#' + code
  return s

def test():
  s = "abcdefg"
  s = s[:3]+"?"+s[4:]
  print(s)  # --> abc?efg

def namechk(dir):
  for name in os.listdir(dir):
    print(fix(name))

args = sys.argv[1:]
if len(args) == 1 and args[0][0] == "-":
  print(usage)
  exit()

if len(args) == 0:
  namechk(".")
  exit()

for dir in args:
  print(dir)
  namechk(dir)

a1.py

実行例

MacBook-Air$ a1.py
c3a.c
test?abc	# 8
c2.c
.DS_Store
test
a1.py
xxx
test?abc
c1.c
b1.c
c4.c
ダ
a1.c
a.out
c3.out
d1.lua
da.txt
??	# 20 20
c3.c
listdir.py
memo.txt
a2.py
test?abc	# a
?	# 20
MacBook-Air$
制御コードあるいは空白は "?" 置き換えて表示されている。その場合 "#" に続いて、コードが 16 進数で示されている。"?" は本当の "?" かも知れない。その場合にはコードは示されない。

ダ問題: NFC / NFD 問題

Apple はようやく unicode の NFC/NFD 問題に常識的な答えを出した。OSX 時代の Apple の考え方では、日本語の文字を含む unicode 文字列の場合には濁点・半濁点を含む場合に問題が発生する。
例えば「ダ」は2つの表現がある。UTF8 文字列で書くと内部コードは各々次の様になる。

	e3 83 80
	e3 82 bf e3 82 99
後者は「タ」に濁点の結合文字を添えた表現で、この考えは昔カタカナで電報を打った時の「タ ゙」と似ている。この時の濁点は1文字として扱われた。昔の電報と違うところは、現在の濁点は「結合文字」としての特殊性を認められているところにある。従って、同じ文字に2つの表現を認めたのである。同じ文字であるから、見え方も同じでなくてはならない。テキストに使われる文字コードは長くなる方で表現しましょう。と Apple は考えたのであった。しかしこの考え方は受け入れられなかった。馬鹿げているからである。
伝統的には短い方で表現されてきた。流石に Apple はこの考えをテキスト一般には主張できなかった。しかし Mac 上のファイル名には適用した。その結果、今日 NFC/NFD 問題と言われている混乱が発生した。問題の理解のために実験してみよう。

「タ」と「ダ」のコードを調べてみよう

どこかに実験用にディレクトリを作る。それを "test" とする。

新しい Mac では

MacBook-Air$ cd test
MacBook-Air$ echo ダ | xd -b
0000000  e3 83 80 0a
0000004
MacBook-Air$ touch タ
MacBook-Air$ touch ダ
MacBook-Air$ ls -l
total 0
-rw-r--r--  1 arisawa  staff  0 11  4 15:49 タ
-rw-r--r--  1 arisawa  staff  0 11  4 15:49 ダ
MacBook-Air$ ls -1 | xd -b
0000000  e3 82 bf 0a e3 83 80 0a
0000008
MacBook-Air$
16進数ダンプに手慣れた Plan9 の xd を使ったが、Mac には既に "od" があるので、代わりに
	od -t x1
としてもよい。
「タ」も「ダ」も 3B でコード化されているのが判る。

他方、古い Mac(mbook) (OSX 時代の Mac) では次の様になる。

mbook$ cd test
mbook$ echo ダ | xd -b
0000000  e3 83 80 0a
0000004
mbook$ touch タ
mbook$ touch ダ
mbook$ ls -1 | xd -b
0000000  e3 82 bf 0a e3 82 bf e3 82 99 0a
000000b
mbook$
「ダ」が 6B (「タ」の 3B + 濁点結合文字の 3B)で表現されているのが判る。

Mac には普通の UTF8 文字列を Mac 固有の UTF8-MAC に変換するツールが備わっているので、それを使って実験してみる。

MacBook-Air$ echo ダ| xd -b
0000000  e3 83 80 0a
0000004
MacBook-Air$ echo ダ| iconv -f utf-8 -t utf-8-mac | xd -b
0000000  e3 82 bf e3 82 99 0a
0000007
MacBook-Air$ echo ダ; echo ダ| iconv -f utf-8 -t utf-8-mac
ダ
ダ
MacBook-Air$
見た目には区別ができない。
Apple は、同じ文字だから同じに見えるべきだと考えた。
しかし、違ったものが同じに見えることは、セキュリティの問題を齎すことがあることに Apple は気付いていなかったのだろう。この問題は僕の昔の記事[5]に詳しい。

[5] URL の日本語表記について
http://ar.nyx.link/iri/

Clang

僕の Mac Book Pro の C コンパイラが gcc から clang になったのは 2017 年である。
このことは
	ls -l /usr/bin
を実行してみれば判る。
	2017 cc -> clang
と表示される。
当時は違和感なく受け入れていた。僕は GNU は嫌いだったし、clang にするならそれでよし。
今回 Mac Book を新調し、macOS Sequoia を動かすと、コンパイラが気になる。
ヘッダファイルを見つけるのに苦労したのである。苦労したのは "dirent.h" なので、以下これに基づいて判ったことを解説する。

何か適当な c のプログラムを a.c として

	cc -v a.c
を実行する。すると cc は大量のメッセージを吐き出す。主要なところは cc に渡されたオプション情報であるが、ヘッダファイルの探し場所の情報も含んでいる。以下の 5 箇所である。

これらのディレクトリの下位にコンパイラが使うヘッダファイルがあるらしい。このことは、プログラム a.c には関係しないらしい。もっとも将来のバージョンでは追加される可能性はあるだろう。
以上、推測口調なのは、この問題に対する正式なドキュメントを知らないからである。
ついでにヘッダファイルを見つけるプログラム hfind を作ったので紹介する。使い方は
	hfind dirent.h
のようにする。結果を次に示す。
MacBook-Air$ hfind dirent.h
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/dirent.h
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/dirent.h
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/Kernel.framework/Versions/A/Headers/sys/dirent.h
MacBook-Air$

次にソースコードを示すが、Plan9port の rc と僕の自作のプログラム lr が使われている。(従って普通のユーザには敷居が高いであろう。Mac の標準的なツールで書けるならその方が良かったのであろうが、僕には知識が不足している。

#!/usr/local/plan9/bin/rc
# you need plan9port and also lr
# look http://p9.nyx.link/netlib for lr
# find header file for clang in macOS

rfork e

usage='usage: hfind header_file'

while(~ $1 -*){
switch($1){
case -*
  echo $usage
  exit
}}

if(! ~ $#1 1){
  echo $usage
  exit
}

t=$1

# clang on Mac searches dirs below:
dirs=(/usr/local/include \
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/16/include \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include \
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks )

# echo $dirs
# for(d in $dirs) echo $d
for(d in $dirs) lr $d >[2] /dev/null | grep $t'$'
exit
-----------------
example usage:
  hfind dirent.h
or more conscientiously
  hfind dirent\.h
なぜ lr を?
lr はディレクトリを再起的に呼び出し、表示する。ls -R はなぜダメなのか。
lr は一つのファイルを1行で表示する。他方 ls -R はそうではない。次に例示する。
MacBook-Air$ lr test
test/
test/.DS_Store
test/bind/
test/bind/.DS_Store
test/bind/dir1/
test/bind/dir1/.DS_Store
test/bind/dir1/bar1
test/bind/dir1/foo1
test/bind/dir1/hoge1
test/bind/dir1/next1/
test/bind/dir2/
...

MacBook-Air$ ls -R test
bind
clang
const
cpython
cpython-main.zip
cython
file.c
...
この問題では dirent.h のパスを知りたいのである。これは(多分) ls -R ではやれない。
「多分」と言ったのは、unix のコマンドのオプションは多すぎて、まじめにマニュアルを読む気になれないからである。何事も、リッチであれば良いものでもないのだ。特に GNU は...

補註:
この ls -R の問題点は文献[6]で議論されている。find を使ったら良いとのことである。多分、zshfind との組み合わせで hfind と同等なものを作れるのではないだろうか?
[6] List files recursively in Linux CLI with path relative to the current directory
https://stackoverflow.com/questions/245698/list-files-recursively-in-linux-cli-with-path-relative-to-the-current-directory

Zsh

僕の古い Mac Book をみると 2017 年から zsh が使われている。あまり気にもしなかったが、今回少しだけ使い心地を調べた。なお zsh に関する基本文献は[7]にある。
なお新しい Mac でシェルを使うときには、Mac のおせっかい(キーボード入力の補完機能)を必ずOFFにする必要があろう。

bash で設定されていた PATH が通っていない
echo $PATH は変化なし
zsh は、この PATH を参照していない

zsh では path になっているとか
https://anatofuz.hatenablog.com/entry/2023/01/08/153838
現在 $path の表示はない
多分 default が使われている

zsh のような大きな shell を使う必要があるのか?
通常は bash でよろし。と思ったがサイズを比較してみると

	sh	101232
	csh	1108448
	bash	1293888
	zsh	1344816
	ksh	2566096
	rc	208760
ここに rc は Plan9port の rc のことである。
サイズに関しては bash と zsh は殆ど違わない。従って zsh を嫌う理由は無さそうである。
次に見る様に zsh は配列を扱える。rc にしかやれなかったことが可能になったか?
arisawa@MacBook-Air% a=(alice bob carol david frank)
arisawa@MacBook-Air% echo $a[2]
bob
arisawa@MacBook-Air% echo $a[2,3]
bob carol
arisawa@MacBook-Air% echo $a[2,4]
bob carol david
arisawa@MacBook-Air% echo $a[2,5]
bob carol david frank
arisawa@MacBook-Air% echo $a[-2]
david
arisawa@MacBook-Air% echo $a[-4,-2]
bob carol david
arisawa@MacBook-Air%
#
# a=(alice bob carol david frank) は(rc と同様)次でもよい
arisawa@MacBook-Air% a=(\
alice \
bob \
carol \
david \
frank)
また
	echo $#a
も rc と同じ

for 文を使った簡単なスクリプト

#!/bin/zsh
a=(\
alice \
bob \
carol \
david \
frank)

for key in $a
  echo $key
シンタックスから想像できる合理的な動作をする。大きな進歩だ。

zsh の ** が素晴らしい。
* は "/" にマッチしない。対して ** はマッチするのだ。僕は Plan9 上の Web server Pegasus を設計した時、そのようなマッチングが欲しかった。しょうがないのでその部分は自分で書いた。

** を使えば dirent.h は次の様に簡単に探し出せる(はずである):

dirs=(/usr/local/include \
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/16/include \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include \
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include \
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks )

for d in $dirs
  ls $d/**/dirent.h
ところが実験していて不思議なことを発見した。
a2.sh:28: no matches found: /usr/local/include/**/dirent.h
a2.sh:28: no matches found: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/16/include/**/dirent.h
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/dirent.h
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/dirent.h
a2.sh:28: no matches found: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/**/dirent.h
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/Kernel.framework/Versions/A/Headers/sys/dirent.h
"no matches" の行が邪魔。これを消すにはどうするか?
Bash の常識では、問題の部分は
for d in $dirs
  ls $d/**/dirent.h 2>/dev/null
で処理できるはずなのだが、期待した通りには働かない。原因不明。
これと類似の問題の報告がNet上に多数上がっている。そのうち記事[8]が一番近そう。

[7] ZSH - Documentation
https://zsh.sourceforge.io/Doc/

[8] In zsh, how do you redirect stderr to /dev/null within a for or foreach loop?
https://serverfault.com/questions/498576/in-zsh-how-do-you-redirect-stderr-to-dev-null-within-a-for-or-foreach-loop

大文字/小文字問題に関するバグ

MacBook-Air$ ls -l a1.c
-rw-r--r--@ 1 arisawa  staff  1365 11  1 09:04 a1.c
MacBook-Air$ ls -l A1.c
-rw-r--r--@ 1 arisawa  staff  1365 11  1 09:04 A1.c
MacBook-Air$ ls -l a*.c
-rw-r--r--@ 1 arisawa  staff  1365 11  1 09:04 a1.c
MacBook-Air$ ls -l A*.c
ls: A*.c: No such file or directory
MacBook-Air$
これは bash の下での実験だが zsh も同様。
こんな簡単な問題でバグっていたら、Apple さん、大丈夫か?
それとも Windows に合わせたか?

責任分担

1台の Mac を考えよう。この Mac には

が関わっている。Mac だけではなく、一般的なコンビューター(サーバーも含めて)における人間関係でもある。
誰が Mac のどの部分(ディレクトリ)に責任を持つのか? はっきりさせておくことは良いことだ。もっと強く、必要なことだ。
OSX 時代の Apple は多少曖昧であった。その前の macOS の時代にはもっと酷かったと思う。インターネット時代において、セキュリティを重視する Apple は厳格化しようと努力している様に見える。
全てのユーザーには、彼らが自由に使える領域が与えられている。それは $HOME 以下の領域である。/Users の下にある。管理者(Bob としよう)が自由に使える領域は Bob の $HOME である /Users/bob/usr/local である。/usr/local は管理者である Bob が Mac のユーザーに対するサービスのために使う。そして Apple は /usr/local/Users を除く全領域に対する支配権を主張する。

伝統的な unix のディレクトリ構成では /etc の扱いが問題になる。ここにはシステム管理に必要な情報が置かれている。情報の書き換えには管理者特権(root 特権)が必要である。Bob の管理を許さざるを得ない。
unix の sudo は極めて危険である。sudo 無しにやれるならその方が安全である。はっきり言えば、sudo はシステムの管理には強力すぎるのである。

ちょっと前までは外から持ってきたソフトウェアをインストールするときには sudo を使うのは当然と考えられていた。ところが最近は sudo を使うと警告が表示され、拒否される。これは正しい考え方である。(ようやく正しい考え方になったというか)。
管理者 Bob の名前でインストールすれば問題はないはずで、それでインストールできないソフトウェアは利用をやめるべきであろう。

sudo を使わないインストーラを作る、あるいは作れる様な Mac を設計するのは難しくないはずである。
OS のバージョンアップのときには sudo は必要だとしても、それをシステム管理においても sudo の要らない Mac を Apple は目指すだろう。