macOS Tahoe(macOS 26)にアップデートしてから、ファストユーザースイッチの調子が悪くて困っていませんか?
私はMacの一台を家族と共用しているのですが、メニューバーから別のアカウントに切り替えようとしても無反応だったり、元の画面に引き戻されたり……。こんな macOS Tahoe環境において、ファストユーザースイッチ機能が不安定になる現象が多くのユーザーから報告されているようです。
Appleはこのバグをなかなか直してくれないのですが、OSの根本的なアップデートが行われるまでの暫定措置として、「Macが切り替え不能になったら、ログインウィンドウのプロセスを強制終了して再起動させる」のが、ユーザー側としてできる有効な回避策になります。
1. 基本編:ターミナルから killall loginwindow を走らせる
一番シンプルで基本的な解決策です。画面が完全にフリーズしておらず、ターミナルが開ける状態であれば、以下のコマンドを叩くだけでログイン画面が安全にリフレッシュされます。
sudo killall loginwindow
これを実行すると、現在の画面が一度暗転し、強制的に初期のログイン画面に戻ります。バックグラウンドのプロセスなどは基本的にそのまま維持されるので、強制再起動(電源ボタン長押し)をするよりはるかに安全です。
ただ、家族にとってはTerminal上での操作はハードルがあるので、別のアプローチが必要です。
2. 応用編:MacOSにサーバーを立ててLAN内からHTTPリクエストを飛ばす
画面が操作不能なときのために、同一ローカルネットワーク(LAN)内にあるスマホや別端末からアクセスするだけで、先ほどのコマンドをリモート実行できる仕組みを構築します。
Macのバックグラウンド(LaunchDaemon)で、軽量なPython HTTPサーバーをroot権限で常駐させます。
2.1 MacのIPアドレスを確認する
まず、LAN内でMacにアクセスするためのIPアドレスを調べます。ターミナルを開いて以下のコマンドを実行してください(Wi-Fi接続の場合)。
ipconfig getifaddr en0
192.168.1.15 のような数字が表示されたら、それがあなたのMacのIPアドレスです。
(※有線LANの場合は en1 などになることがあります。システム設定 > ネットワーク > 詳細 からも確認可能です)
2.2 Python Webhookサーバーの作成
以下のスクリプトを /usr/local/bin/mac_reset_server.py として作成し、実行権限(sudo chmod +x)を付与します。root権限で実行されるため、スクリプト内でsudoは不要です。
/usr/local/bin ディレクトリがない場合には、あらかじめ sudo mkdir -p /usr/local/bin などとしてディレクトリを作る必要があるかもしれません。
from http.server import BaseHTTPRequestHandler, HTTPServer
import os
# 簡易的なセキュリティトークンとポート番号の設定
SECRET_TOKEN = "pico-reset-1234"
PORT = 8080
class ResetHandler(BaseHTTPRequestHandler):
def do_GET(self):
# トークンが一致した場合のみ実行
if self.path == f"/reset?token={SECRET_TOKEN}":
self.send_response(200)
self.end_headers()
self.wfile.write(b"Success: loginwindow killed.")
# root権限で動作するため killall を直接実行
os.system("killall loginwindow")
else:
self.send_response(403)
self.end_headers()
self.wfile.write(b"Forbidden")
server = HTTPServer(("0.0.0.0", PORT), ResetHandler)
server.serve_forever()
2.3 LaunchDaemonの設定と起動
システム起動時に自動実行するため、/Library/LaunchDaemons/com.custom.macreset.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.custom.macreset</string>
<key>ProgramArguments</key>
<array>
<string>/usr/bin/python3</string>
<string>/usr/local/bin/mac_reset_server.py</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
</dict>
</plist>
ターミナルで以下のコマンドを実行し、サービスを登録・起動します。
sudo chown root:wheel /Library/LaunchDaemons/com.custom.macreset.plist
sudo chmod 644 /Library/LaunchDaemons/com.custom.macreset.plist
sudo launchctl bootstrap system /Library/LaunchDaemons/com.custom.macreset.plist
sudo launchctl start com.custom.macreset
2.4 スマホからアクセスしてみる
Macと同じWi-Fi(LAN)に接続したスマートフォン上でSafariやChromeなどのブラウザを開き、以下のURLにアクセスしてください。(IPアドレスは3.1で調べたものに置き換えてください)
http://<MacのIPアドレス>:8080/reset?token=pico-reset-1234
画面に Success: loginwindow killed. と表示され、Mac側の画面が強制的にログイン画面に戻れば成功です。
このページをスマホの「ホーム画面に追加」しておけば、ファストユーザースイッチが聞かないときの解決ボタンとしてワンタップで使えるようになります。
3. 発展編:M5Stampを利用して物理スイッチを作る
でもできれば、Macのそばに物理ボタンとしてあるといいですよね。私は、手元に転がっていたM5Stamp C3を配置しておき、必要なときに家族がボタンを押すだけで killall loginwindow が走らせるようにしました。
M5Stamp C3とは?
M5Stamp C3は、切手サイズの超小型マイコンです。Wi-Fi機能(ESP32-C3チップ)を備えており、フルカラーLEDや物理ボタンも最初から付いているため、ちょっとしたIoTスイッチを作るのに最適です。
ほかの M5Stamp C3U や M5Stamp Pico などでも、ほぼ同様に使えるはずです。
Arduino IDEの準備
- Arduino IDEをMac/PCにインストールします。
- ボードマネージャーからESP32の環境を追加し、ボードとして「M5Stamp C3」を選択します。
- ライブラリマネージャーから
Adafruit NeoPixelを検索してインストールします。
コードの書き込み
以下のコードをコピペし、Wi-Fi情報とMacのIPアドレスを書き換えてM5Stampに書き込みます。
#include <WiFi.h>
#include <HTTPClient.h>
#include <Adafruit_NeoPixel.h>
// --- 設定項目 ---
const char* ssid = "YOUR_WIFI_SSID";
const char* password = "YOUR_WIFI_PASSWORD";
const char* serverUrl = "http://<MAC_IP_ADDRESS>:8080/reset?token=pico-reset-1234";
const int buttonPin = 3; // M5Stamp C3 本体中央の物理ボタン
const int ledPin = 2; // 内蔵LEDのピン番号
const int numPixels = 1;
Adafruit_NeoPixel pixel(numPixels, ledPin, NEO_GRB + NEO_KHZ800);
void setup() {
Serial.begin(115200);
unsigned long start = millis();
while (!Serial && (millis() - start < 5000)) { delay(10); }
pinMode(buttonPin, INPUT_PULLUP);
pixel.begin();
pixel.setBrightness(20);
pixel.show();
// Wi-Fi接続開始(接続中は黄色に点滅)
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
pixel.setPixelColor(0, pixel.Color(255, 150, 0));
pixel.show();
delay(250);
pixel.setPixelColor(0, pixel.Color(0, 0, 0));
pixel.show();
delay(250);
}
// 接続成功時は青に点灯
pixel.setPixelColor(0, pixel.Color(0, 0, 255));
pixel.show();
delay(500);
pixel.setPixelColor(0, pixel.Color(0, 0, 0));
pixel.show();
}
void loop() {
if (digitalRead(buttonPin) == LOW) {
// 送信中は白に常時点灯
pixel.setPixelColor(0, pixel.Color(255, 255, 255));
pixel.show();
if (sendResetRequest()) {
flashLED(0, 255, 0, 5000); // 成功: 緑で5秒点滅
} else {
flashLED(255, 0, 0, 5000); // 失敗: 赤で5秒点滅
}
}
}
bool sendResetRequest() {
if (WiFi.status() != WL_CONNECTED) return false;
HTTPClient http;
http.begin(serverUrl);
http.setTimeout(5000);
int httpCode = http.GET();
bool isSuccess = false;
if (httpCode == 200) {
String payload = http.getString();
if (payload.indexOf("Success: loginwindow killed.") != -1) {
isSuccess = true;
}
}
http.end();
return isSuccess;
}
void flashLED(uint8_t r, uint8_t g, uint8_t b, unsigned long duration_ms) {
unsigned long startTime = millis();
while (millis() - startTime < duration_ms) {
pixel.setPixelColor(0, pixel.Color(r, g, b));
pixel.show();
delay(250);
pixel.setPixelColor(0, pixel.Color(0, 0, 0));
pixel.show();
delay(250);
}
}
このようなやり方で、家族が比較的快適にログインウィンドウを強制的に機能させることができました。
Tahoeのバグに悩まされている方は、ぜひお試しください。