【Mac】コマンドラインでGUIからログアウトする。

# はじめに
ある日Macの上部にあるメニューがクリックしても反応しない謎の事象が発生。
その他一部GUIの表示などが崩れていたものの、基本的なキーボード操作やマウス操作は効いたため、コマンド操作によりログアウトできないだろうかと考えた。

:::note warn
[Coomand] + [Ctrl] + [Q]によりログインウィンドウへ一度戻り再ログインするだけでは事象は解決しなかった。
:::

# TL;DR
- Macのlogoutコマンドはexitコマンドと同じ。
- ログアウトの機能を提供する直接的なコマンドはない模様
- ログアウトはlaunchctl bootoutコマンドを利用して実現する

以下のコマンドでログアウト可能。
```zsh:logout
launchctl bootout user/$(id -u)
```

## logoutコマンド

zshの場合ビルトインコマンドとして"logout"コマンドが用意されております。
一見いかにもログアウトできそうなコマンドですが、
このコマンドはexitコマンドと同じ動きとなり、
アクティブシェルを閉じるだけで、GUIからログアウトを行うことはできませんでした。
マニュアルにも"same as exit"と記載されております。

## launchctl bootout

### launchctl
launchctlはlaunchdを操作するためのコマンドラインツールです。
launchdはMacのサービス(デーモン)を管理しているような存在で、
誤解を恐れずに言えば、RHELでいうsystemdのようなものと捉えていただければいいと思います。

launchctlはサブコマンド形式の構文となります。
ログアウトを行う場合、サブコマンドとして"bootout"と指定します。

```zsh:書式
launchctl サブコマンド
```

### bootout
launchctlのサブコマンドの一つです。
bootoutは特定のサービスをアンロードします。

``` zsh:書式
launchctl bootout domain-target [service-path service-path2 ...] | service-target
```
#### domain-target
launchdが管理するプロセスグループのようなものと認識してます。
以下の3つが存在するようです。
- system
- user\/\<uid\>
- gui\/\<uid\>

つまりアンロードさせる対象がどの領域かによって指定するドメインが変わるのでしょう。
ドメインがsystemにあたるものはプロセスの実行ユーザがrootとなっていものと考えられます。
まさにlaunchdなんかはこのドメインに属すると推測できます。

userとguiはどちらも任意のユーザによって実行されたプロセスを対象とする際に指定しますが、
userの場合ログインの有無は問わないのに対して、
guiの方はGUIログインしているアクティブなユーザのみを対象とした領域となるようです。
\<参考\>
[launchctl/launchd cheat sheet](https://gist.github.com/masklinn/a532dfe55bdeab3d60ab8e46ccc38a68)

今回のケースではbootoutの引数としてはdomain-targetのみを指定します。
> If no paths or service target are specified, these commands can
either bootstrap or remove a domain specified as a domain
target. Some domains will implicitly bootstrap pre-defined paths
as part of their creation.
>
>引用：launchctlのhelpよりbootstrap|bootout箇所を抜粋

service-pathやservice-targetを指定しない場合、
domain-targetで指定されたドメインをremoveすると記載があります。
removeという表現が誤解を招きそうですが、
指定したドメインのプロセスを全てkillする（終了させる)くらいに捉えていただくのがいいと思います。
決して特定のユーザアカウント削除するような動きではありません。
これにより、対象ユーザを親とするプロセスがkillされることで、結果としてログアウトが実現されるものと捉えます。

今回は任意のユーザーのログアウトをということで、
ドメインは"user"とします。
加えてどのユーザーを対象とするかUIDで指定する必要があります。

##### UIDの取得
現在ログインしているユーザのUIDは以下のコマンドで取得可能です。
```zsh:UIDの取得
id -u
```
余談ではありますがMacの場合、一般的なUNIX系環境でアカウント情報が載っている"/etc/passwd"には
MacOSのユーザーアカウントの情報が載っていないので注意してください。
MacOSのアカウント情報は内部的にはLDAPとパスワードサーバで管理されているようで、
Open Directoryというディレクトリサービスを通じてアクセス・管理が行われているようです。

## コマンドの実行
上述の説明を踏まえ、以下がログアウトの実行コマンドとなります。
```zsh:ログアウトコマンド
launchctl bootout user/$(id -u)
```
::: note alert
私の環境ではスーパーユーザーの権限は不要でしたが、
パーミッションエラーが出る場合sudoで実行してください。
```zsh:パーミッションエラーサンプル
Boot-out failed: 1: Operation not permitted
```
:::

"id -u"により自身のUIDを指定しておりますが、
ここで他のログインユーザのUIDを指定すれば、
ログアウトさせることもできます。

鋭い方は「gui」ドメインでも実行できるのではと勘づかれているかもしれません。
userドメインとguiドメインの違いはGUIログインの有無によるものですので、
GUIログインしているユーザをログアウトさせる場合問題なくログアウトできます

```zsh:guiドメインバージョン
launchctl bootout gui/$(id -u)
```
ただし、CLIログインしているユーザをguiドメイン指定でログアウトさせることはできません。
CLIログインしているユーザ上でドメインをguiに指定してbootoutさせた結果が以下です。
```zsh:CLiログインでbootout
launchctl bootout gui/$(id -u)
#実行結果
Domain does not support specified action
```
# 環境
OS:macOS 13.3(Ventura)
シェル：zsh 5.9

# 終わりに
ログアウトなんてコマンド1つ打ってOKくらいのイメージでしたが、
思ったより奥が深いですね。
launchctlのドメインという概念もこの件で初めて知りましたし、
MacOSのアカウント情報はOpenDirectoryというディレクトリサービスを通じて行われているというのも初めて知りました。

# 参考
https://gist.github.com/masklinn/a532dfe55bdeab3d60ab8e46ccc38a68