こんにちわ。
先日、新しいAndroid端末を購入したので、とりあえずTermuxとNode.jsを入れたのですが、思わぬ壁が大きく立ちはだかる事となってしまいました。
概要
表題の通りですが、Android8.0でNode.jsのrequire('os').cpus()
を走らせるとundefined
が返ってきます。
確認端末はGALAXY S9+(SM-G9650)です。
原因
Android8.0から、/proc/
配下の各仮想ファイルへのアクセスが大きく制限されてしまった事によります。
cpus()
は/proc/stat
を参照しますが、これがアクセス禁止対象となってしまったのです。
ちなみに、/proc/cpuinfo
などは従来通り参照可能です。
Google曰く
/proc/stat
は別アプリケーションの稼働状態を推測可能にし、すなわちサイドチャネル攻撃の危険性がある為、アクセス禁止にした(意訳)
との事です。
弊害
まずnpm
コマンドが使えません。
内部でcpus().length
が使われている為、異常終了してしまいます。
つまり、npm install
さえ出来ないのです。
他モジュールについても、cpus()
を使う物に関しては全滅です。
色々と致命的です。
対処方法(暫定)
npm
コマンド禁止縛りは流石にお話にならないので、かなり強引ですが暫定処置があります。
// before
maxConcurrentWorkers: require('os').cpus().length
// after
maxConcurrentWorkers: 8 // CPUコア数で決め打ち(SDM845は8コア)
幸いnpm
内部でcpus()
を使う場所はここだけなので、手っ取り早くハードコードしてしまいます。
const execSync = require("child_process").execSync;
require("os").cpus = ()=>{
return execSync("grep Hardware /proc/cpuinfo | sed -r -e \"s|^.*?: ||\"").toString();
}
require("os").cpus().length = Number(execSync("grep processor /proc/cpuinfo | wc -l").toString());
cpus().length
は使用頻度が高い為、/proc/cpuinfo
からコア数を抽出して、標準APIに上書きする事も可能です。
おわりに
根本解決するには、Termux向けNode.jsのビルド時に別ファイルを参照するよう改変するか、端末をroot化するかの2択です。
上述にもある通り、Android8.0はセキュリティが大幅強化された為、root化は無茶苦茶しんどい上に、ファームウェアバージョン等の条件もかなり限定されます。
(SM-G9650のroot化も途中で断念しました)
そんなこんなで、根本解決は現状ほぼ不可能という、悲しい事実。
自由度の高さがウリだったAndroidも、だんだん林檎化が進んでいるように感じ、少し寂しく思います。