Apple の脅威の通知と金銭目当てのスパイウェアへの対策について

しばらく返答が寄せられていないようです。 再度ディスカッションを開始するには、新たに質問してください。

シェルスクリプトの動作について

質問は2 つです。シェルスクリプトを起動時にlaunchd から読んだ際にType A とType B の動作の違いは何故か。

memory_pressure コマンドを読んだ際に、再起動前のFinder のウインドウが開かなくなるのは何故か。

です。


状況ですが、

勉強がてら下記のシェルスクリプトを作りました。勉強しながら作ったので、その手の人が見るとダメダメな可能性が高いすが、どうにも不思議な動きをするので質問することとしました。

下記のシェルスクリプトはThunderbolt 接続の外付けSSD から起動したさいにVM パーティションをマウントするためのものです。

Type A

#!/bin/bash -x



set -eu



UUID=「自分のところのUUID」

mountpoint=""

tpoint="/private/var/vm"

vmSwap="vm.swapusage: total = 0.00M"

swapfile="vm.swapusage: total = 0.00M"



sleep 7



for n in $(seq 1 5)

do

if [ "$mountpoint" = "$tpoint" ] ; then

echo $((n - 1)) >> /Users/Shared/n.txt

else

set +e

diskutil mount -mountPoint /private/var/VM "$UUID"

set -e



sleep 4

mountpoint=$(diskutil info "$UUID" | grep -o "$tpoint")

echo ${mountpoint}" xx" >> /Users/Shared/n.txt

fi

done



set +e

memory_pressure -l critical 2>&1 &

set -e



sleep 7



set +e



for n in $(seq 1 61)

do

if [ "$swapfile" = "$vmSwap" ] ; then

sleep 2

swapfile=$(sysctl vm.swapusage | grep -o "$vmSwap")

else

killall -15 -e memory_pressure

exit 0

fi

done

それでType B

#!/bin/bash



UUID=「自分のところのUUID」

mountpoint=“”

mou="vate/var/vm (apfs, local, nodev, nosuid, journaled, noowners, nobrowse)"



for n in `seq 1 14`

do

if [ "$mou" = "$mountpoint" ] ; then

echo $n >> /Users/Shared/Logs/n.txt

exit

else

diskutil mount -mountPoint /private/var/vm $UUID

fi



sleep 3

mountpoint2=`mount | grep "s4 on /private/var/vm (apfs,"`

mountpoint=${mountpoint2##*pri}

done

提載状態なら、どちらも正常に動きます。

それで、

最初の質問はType A の8 行目「sleep 7」です。Mac によってはこの「7」が4 以下になると動きません。動かないのにエラーは出ません。ログによれば目的のパーティションはマウントされて終了しています。ところがログイン後、確認するとマウントされていません。

これは何故でしょうか。これが第一の質問です。


第二の質問ですが、Type A のmemory_pressure コマンドです。

何故かType A の上記のコマンドを実行するとMac を起動してログインしたさいに再起動前に開いていたウインドウが開かなくなる症状があります。私のところでは再現性は高いのですが、気まぐれに起きなくなったりします。気まぐれに直るのは良いのですが何故、起きるのでしょうか。


macOS High Sierra 10.13.4

投稿日 2018/04/15 20:44

返信
返信: 29

2018/04/19 13:24 ni_ki への返信

> 管理者権限だとsudo が必要になるので


あ、そうでしたっけ。思いつきで書いてしまって申し訳ないですw


外付けのSSD持っていないので、インターナルSSDで試してみました。インターナルの場合、正常にマウントされるので、はじめにumoutしています。


com.example.vmmount.plist

<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>Label</key> <string>com.example.vmmount</string> <key>LaunchOnlyOnce</key> <true/> <key>LowPriorityBackgroundIO</key> <true/> <key>ProgramArguments</key> <array> <string>/Library/PrivilegedHelperTools/vmmount.sh</string> </array> <key>RunAtLoad</key> <true/> </dict> </plist>

vmmount.sh

#!/bin/bash -u vmlog() { echo "$(date '+%b %d %H:%M:%S') ${0##*/}: $1" >>/Library/Logs/vmmount.log } vmlog "Start..." while [ "$(ps -A -o command | grep -c loginwindow)" = 0 ]; do vmlog "Waiting for loginwindow to launch..." sleep 1 done mounted=$(df -b -T apfs | grep -c /dev/disk1s4) if [ $mounted -eq 1 ]; then vmlog "Unmount APFS Volume VM" umount /dev/disk1s4 fi mounted=$(df -b -T apfs | grep -c /dev/disk1s4) if [ $mounted -eq 0 ]; then diskutil mount -mountPoint /private/var/vm /dev/disk1s4 >/dev/null 2>&1 if [ $mounted -eq 0 ]; then for n in $(seq 0 15); do if [ $mounted -eq 1 ]; then vmlog "APFS Volume VM on /dev/disk1s4 Mounted." break else mounted=$(df -b -T apfs | grep -c /dev/disk1s4) fi sleep 1 done fi if [ $mounted -eq 1 ]; then for n in $(seq 0 15); do if [ $mounted -eq 0 ]; then vmlog "APFS Volume VM on /dev/disk1s4 Unmounted." break else mounted=$(df -b -T apfs | grep -c /dev/disk1s4) fi sleep 1 done fi else vmlog "APFS Volume VM already mounted." fi vmlog "Stop..." exit 0

vmmount.log

ユーザがアップロードしたファイル


最初に"loginwindow"が立ち上がるまで待つルーチンを入れてみましたが、3回試した内の1回しか実行されませんでした。Unmountを検出するルーチンは一度も実行されず。リスタートする前に起動させていたのはTerminalのみですが、画面にあるとおりリストアーされてます。


システム終了前に開いているアプリの情報は、~/Library/Saved Application Stateに保存されるのでVMとは無関係に思うのですが、何なのでしょうね?

2018/04/17 14:06 ni_ki への返信

ディスク情報を得るコマンドは、df、mount、diskutilなどがありますが、以前、私が試した限りではdfが一番はやかったです。


8行目のsleepがないと動かないとのことですが、不可解ですね。seqの数を増やしてもだめなんですか?

また、"diskutil mount"を何度も発行するのは良くないと思います。


というわけで、私ならこうします。


device=$( diskutil mount -mountPoint /private/var/vm $UUID | swk '{ print $4 }') mounted=0 if [ $device != "" ]; then for n in $(seq 0 120); do if [ $mounted -eq 1 ]; then echo $n >> /Users/Shared/Logs/NumberOfSeconds.txt break else mounted=$(df -b -T apfs | grep -c $device) fi sleep 1 done fi


memory_pressureはわかりません。

2018/04/17 20:04 hohokihai への返信

hohokihai さんによる書き込み:


8行目のsleepがないと動かないとのことですが、不可解ですね。seqの数を増やしてもだめなんですか?

厳密には動かないのではなくログによれば1 回目のdiskutil mount の実行でマウントを完了して正常終了しているようです。ところがログインが完了してから確認するとマウントされていません。seq の方は1 回目ので正常完了とされているので増やしても動作に変わりありません。

というわけで、私ならこうします。


device=$( diskutil mount -mountPoint /private/var/vm $UUID | swk '{ print $4 }') mounted=0 if [ $device != "" ]; then for n in $(seq 0 120); do if [ $mounted -eq 1 ]; then echo $n >> /Users/Shared/Logs/NumberOfSeconds.txt break else mounted=$(df -b -T apfs | grep -c $device) fi sleep 1 done fi


確認してみたのですがdf コマンドの行の「grep -c $device」ですが、このgrep はUUID が含まれる行数を計算するものでしょうか。

2018/04/18 15:07 ni_ki への返信

実は私、起動時にRamdiskをmountするアプリを作っているのですが、Yosemiteの頃からmacOSが勝手にunmountするようになったので、mountする直前にdiskarbitrationdやfseventsdをkillして凌いでいました。(それでもunmountされてしまうこともありました)


Yosemiteではsystem.logにkernelがunmountしたとのログが残っていましたが(mountしてから約8秒後)、Sierraではsystem.logにも記録されなくなりました。これらはセキュリティ強化の一環なのでしょう。


その後、フォーマットをHFS+からGPTに変えたら、小細工せずともunmountされなくなったので(High Sierraにした現在でも動いています)、GPTコンテナの中にあるAPFSも問題ないと思ったのですが、今回の場合ももしかするとmacOSが勝手にunmountしているのかもしれません。


mountした直後に、macOSが勝手にunmountしているのであれば、diskutil mountを連続して行うのも仕方がないのかもしれません。しかし何故、連続で行えば成功するのか釈然としません。


> dfコマンドの行の「grep -c $device」ですが、このgrepはuuidが含まれる行数を計算するものでしょうか。


$deviceには「disk1s4」が入っている筈です。従って、grepは「disk1s4」が含まれる行数です。


launchd.plistは、/Library/LaunchDaemonsに入れてます?/Library/LaunchAgentsですか?


ところで今回の動機は、APFSでフォーマットした外部SSDから立ち上げたとき/var/vmが起動時にマウントされない問題絡みですよね?


MacBookであれば、下記を設定すればバッテリーがなくならない限りスリープしなくなるので、/var/vm/sleepimageの書き換えはまず起こらなくなり、/var/vmをマウントせずとも平気かもしれません...?


sudo pmset -a autopoweroffdelay 90000 # 25h

sudo pmset -a hibernatemode 0

sudo pmset -a standby 0

2018/04/18 23:01 hohokihai への返信

やっぱりUUID ですよね。マッチングする文字列を変更いたしました。ただやはり最初にsleep 5 とかを入れないと不安定なようです。

また再起動でウインドウがすべて閉じる症状はmemory_pressure コマンドが原因ではないようです。hohokihai さんのスクリプトでも発生しました。何かのタイミングのようですね。

またお示しのVM パーティションの4.3GB はパーティションの使用量だと思われます。

私にところで試した際には30GB くらいまではスワップいたしました。

Launched のplist の置き場所は試してみます。

ありがとうございます。

2018/04/22 14:24 ni_ki への返信

> 前にも書き込んでいますが、やはりdiskutil mount コマンドを使う前に時間を置くのが最善なようです。

> sleep 17

> くらいから、我が家の複数のMac で安定します。


そうですか。前のサンプルではloginwindowが起動するのを待ちましたが、それよりも後に起動するWindowServerやSystemUIServerを待つというように、プロセスを変えるのも手だと思います。

2018/04/22 21:49 hohokihai への返信

それらのプロセスがある時から17 秒後か、その前かで動くかどうか決まりそうですね。

もっとも「ある時」がいつかはわかっていませんから起動時のOS のログとシェルスクリプトの起動時間を比べれば何かわかるのかも知れないです。

今回の件ではシェルスクリプトが動かないのではなくマウント後に何かがマウント済みのvm パーティションをアンマウントしているようですので、そこら辺調べると面白そうです。

時間が取れたら調べてみます。

2018/04/22 22:39 ni_ki への返信

今更気付いたのですが、2 つ目の質問の方はhohokihai さんのスクリプトでも起きたのでmemory_pressure コマンドは無関係かと思っていましたが、私の方のスクリプトでこのコマンドを別のスクリプトにしてさらにそのスクリプトを呼び出すApple Script で作成したアプリケーションで呼び出すようにすると起きなくなります。つまり私の方にスクリプトでは、memory_pressure が症状に関わったいるのは間違いないようです。

ただ、アプリケーションを介さずに直接、別スクリプトを呼ぶと症状は解決しません。

2018/04/23 01:39 ni_ki への返信

試しにvm フォルダにdate コマンドの出力を書き込むスクリプトを作って書き出しが行われる時間とログを観察してみました。

どうもloginwindow が起動してから2 から3 秒後にマウントを正常に受け付けるのではないかと思える状況です。なのでwhile done の次にsleep 4 くらいで最速マウントできるかも知れません。

ただloginwindow の起動はかなり早い段階で行われると思いますので単純にsleep 17 と大きな差は出ないかも知れません。

ちなみに今回、loginwindow をInstaller Progress なるプロセスが呼び出すらしいことを初めて知りました。

なお時間も遅いのでスクリプトでの検証はしていません。検証は時間をみて行わせていただきます。

2018/04/23 19:24 ni_ki への返信

以前、fseventsdやdiskarbitrationdをmount直前にkillしたら、unmountされなくなったという経緯を書きました。これらが何をしているか見ていきましょう。


・fseventsd (File System Events: FSEventStreamRef)


Get notifications when the contents of a directory hierarchy change.


The file system events API provides a way for your application to ask for notification when the contents of a directory hierarchy are modified. For example, your application can use this to quickly detect when the user modifies a file within a project bundle using another application. (XcodeのDocumentsより)


・diskarbitrationd


diskarbitrationd listens for connections from clients, notifies clients of the appearance of disks and filesystems, and governs the mounting of filesystems and the claiming of disks amongst clients. (man diskarbitrationdより)


Notifications(通知)とは、プロセス間でやりとりするメッセージのようなものです。例えば、CocoaアプリケーションではNSWorkspaceというフレームワークがあり、ディスクがマウント/アンマウントしたことを通知してくれて、その時に処理を加えることができます。(例えば、音楽CDが挿入されたら再生を始めるなど)


この機能を提供しているのがfseventsdやdiskarbitrationdと推測しています。これらは機能ベースであり、fseventsdやdiskarbitrationdなどからNotificationsを受け取って、実際にunmountしているのは別のプロセス(デーモン)だと思うのですが、特定することはできませんでした。


sleep値を増やして動くのであればそれで良いと思いますけども、ついついmacOSの仕組みを暴きたい欲求に駆られてしまいまして...

2018/04/23 20:55 hohokihai への返信

情報ありがとうございます。

hohokihai さんによる書き込み:


sleep値を増やして動くのであればそれで良いと思いますけども、

私もhohokihai さんのWindouwServer とかを試したほうが良いのではないかと言われる前はsleep で十分だと思っていたのですが、

ついついmacOSの仕組みを暴きたい欲求に駆られてしまいまして...

仕組みを少しでも理解していたほうが、もしマイナーバージョンアップで動かなくなった際に融通が効く気がしているので可能なら解析したいところです。

ただ私はプログラム系やシステム系はダメダメなので、おそらく深層には到達できないだろうと想像しております。


それで内蔵ストレージがSSD のモデルで確認したところ、VM フォルダにシェルスクリプトで書き込めませんでした。権限が無いというエラーが返ってきました。

外付けSSD だと最初の数秒間だけ「権限が無い」が返ってきますので、やはり最初はマウントしているようです。

それにしてもLaunchDaemons に置けばroot 権限だと思っていたので、その状態でも権限無しというのは難しいですね。

2018/05/08 21:57 ni_ki への返信

ここ最近のGW で、今回のスクリプトを考察していたらマウントした際にnoatime,noexec でマウントされていないことがやはり気になりました。

ということで先のスクリプトを改良してnoatime,noexec でマウントする様にいたしました。

ただ、

diskutil コマンドはnoatime の設定ができない様です。それができるmount コマンドはUUID でのマウントができない様です。

ということで、

/dev/diskxsx を取得する様にしてスクリプトを作成いたしました。初めはこれで完了と思ったのですが、

質問の2 つ目が、sleep コマンドで回避できません。

ということで、意見を求めます。知見のあるかたの書き込みをお待ちいたします。

2018/05/10 00:34 hohokihai への返信

情報ありがとうございます。

本日も1時間ほど格闘してみたら、sleep を120 長にしても変化ないことがわかりました。どうやら呼び出されていること自体が問題も起こすようです。

ただ、症状が起きてからlaunchd から外しても改善しない時がありました。

なので、かなり根が深い問題のようです。

要するに一度、OS 側に起動時にVM をマントする行為が検知されると、異常な動作をするようになると言うことかと、現状は思えます。

2018/05/19 18:18 ni_ki への返信

今更、気付きました。

スクリプトと無関係に起動時にVM パーティションをマウントしている表示(Verbose モード。)が出ておりました。

「apfs_vfsop_mount:1449: mounted volume: VM」

だそうです。この数行前には、

「VM Swap Subsystem in ON」

とも表示していました。

このログにもっと早く気づいていれば、一度マウントした後にアンマウントされている事に気付けたかもしれません。観察が甘かったです。

ただしHFS+ から起動した際に、このログがあるのかは確認しておりません。VM パーティションが無いはずですから出ないのではと思っておりますが。

2018/06/14 23:40 ni_ki への返信

macOS High Sierra 10.13.4 から10.13.5 にアップデートする際に不測の事態を避けるため、このスクリプトは外してからアップデートしようと思っていたのですが、すっかり忘れてアップデートしてしまいました。

今、ログを確認したらアップデートの再起動時にmount コマンドがエラーで止まっていました。権限無しだったようです。

二台で確認しましたが、一台はファームウェアのアップデート音がしていました。何事も起きずにアップデート出来て良かったです。次のアップデート時には忘れずに外すようにしたいと思います。

それとも必ずエラーで止まってくれるのかも。

シェルスクリプトの動作について

Apple サポートコミュニティへようこそ
Apple ユーザ同士でお使いの製品について助け合うフォーラムです。Apple ID を使ってご参加ください。