OSC 3008 とは:systemd の端末コンテキスト信号と文字化けの対処

UAPI.15 OSC 3008 Hierarchical Context Signalling の役割、送信されるデータ、systemd での使われ方、古い端末や踏み台、非対応環境で文字化けが出るときの確認と無効化方法を説明する。

OSC 3008、正式には Hierarchical Context Signalling は、UAPI Group の仕様 UAPI.15 です。端末セッション内の現在の「コンテキスト階層」を、プログラムが端末エミュレーターへ伝えるための新しい端末制御シーケンスを定義しています。

簡単に言うと、現代の Linux 端末でよく出てくる「いま自分はどの階層にいるのか」という問題を解決しようとするものです。

たとえば、ローカルマシンからサーバーへ SSH し、さらにコンテナへ入り、その後 run0 や類似ツールで権限昇格したコマンドを実行することがあります。ユーザーから見ると 1 つの連続した端末ウィンドウですが、端末エミュレーターから見ると、追加の信号がなければ現在の出力がローカル、リモート、コンテナ、権限昇格後のコマンドのどれに属するのかを正確に知るのは難しいです。

OSC 3008 はこのような場面のために設計されています。UAPI の公式仕様では、端末エミュレーターが画面上の現在内容のコンテキスト階層を追跡できると説明されています。systemd も対応実装を提供しており、通常は 80-systemd-osc-context.sh というスクリプト名で使われます。

公式仕様:

OSC 3008 が必要な理由

従来の端末は基本的にプログラムの出力を表示するだけです。せいぜいウィンドウタイトル、色、プロンプトで環境の違いを補助する程度でした。しかし、現代の端末セッションは多層にネストされがちです。

1
local shell -> SSH remote server -> Docker/Podman/systemd-nspawn container -> run0 elevated command

PS1 だけに頼ると、いくつか問題が出ます。

  1. プロンプトは現在の shell しか表しにくく、ネスト階層全体を表現しづらい。
  2. shell、ディストリビューション、ユーザー設定によって互いに上書きされる。
  3. 端末エミュレーターが、どの出力がどのコンテキストに属するかを確実に判断できない。
  4. 一部の制御シーケンスが非対応端末でそのまま文字化けとして表示される。

OSC 3008 は、より構造化された方法を提供します。コンテキストへ入るプログラムが start シーケンスを送り、コンテキストから出るプログラムが end シーケンスを送ります。端末エミュレーターはそれを解析し、現在の出力がどのコンテキストノードに属するかを把握できます。

どんな情報を送るのか

OSC 3008 は OSC エスケープシーケンスの集合です。仕様では、次の 2 種類の中核コマンドが定義されています。

1
2
OSC "3008;start=..." ST
OSC "3008;end=..." ST

start はコンテキストの開始、更新、または再び現在のコンテキストになったことを表します。end はコンテキストの終了を表します。

各コンテキストにはメタデータフィールドを付けられます。よく使われるフィールドは次のとおりです。

  1. type=:コンテキスト種別。例:shellcommandremotecontainervmelevate
  2. user=:シーケンスを送った UNIX ユーザー名。
  3. hostname=:ホスト名。
  4. machineid=/etc/machine-id 由来の machine ID。
  5. bootid=/proc/sys/kernel/random/boot_id 由来の boot ID。
  6. pid=:シーケンスを送ったプロセスの PID。
  7. comm=:プロセス名。
  8. cwd=:現在の作業ディレクトリ。主に shellcommand 用。
  9. cmdline=:対話的に呼び出されたコマンドライン。
  10. container=vm=:コンテナ名または仮想マシン名。
  11. targetuser=targethost=:対象ユーザーまたはリモート対象ホスト。

これらのフィールドはユーザーが直接読むためのものではなく、端末エミュレーターが解析するためのものです。対応端末はこれらの制御シーケンスを消費し、UI 体験の改善に使います。

何が便利になるのか

端末エミュレーターが OSC 3008 に対応していると、より賢い表示ができます。

第一に、異なるコンテキストの出力を区別できます。たとえばコンテナ内の出力、権限昇格コマンドの出力、リモート SSH の出力に、別の背景、枠、ヒントを付けられます。

第二に、階層のパンくず表示ができます。端末は現在の状態をおおよそ次のように把握できます。

1
local machine -> remote host -> container -> root command

これはウィンドウタイトルや PS1 だけに頼るより安定しています。

第三に、ウィンドウタイトル、タブ、コンテキストメニューの補助にも使えます。現在のコンテキストに応じてタブタイトルを更新したり、特定の出力に対して「同じディレクトリで shell を開く」といった操作を提供できます。

第四に、誤操作を減らせます。たとえば本番環境のコンテナ内で root コマンドを実行している場合、端末がより目立つ警告を出し、危険なコマンドを間違った環境で実行する可能性を下げられます。

systemd はどう組み込んでいるのか

systemd の実装は主に 80-systemd-osc-context.sh にあります。このスクリプトは profile の仕組みで読み込まれ、対話的 Bash 環境で関連関数とプロンプトフックを設定します。

よくあるパスは次のようなものです。

1
2
/etc/profile.d/80-systemd-osc-context.sh
/usr/lib/systemd/profile.d/80-systemd-osc-context.sh

実際の場所はディストリビューションによって異なります。systemd のソース中のコメントでは、このファイルは systemd-tmpfiles によって有効化され、/etc/profile.d/ へリンクされると説明されています。

Bash では、次のような関数名が使われます。

1
2
3
__systemd_osc_context_escape
__systemd_osc_context_common
__systemd_osc_context_precmdline

同時に PROMPT_COMMANDPS0 にも影響します。PS0 は Bash がコマンドを読み取った後、実行する直前に展開されます。そのため、非対応端末では各コマンド実行前に長い 3008;start=... テキストが表示されることがあります。

なぜ一部の端末では文字化けするのか

通常、端末エミュレーターは OSC 3008 を制御シーケンスとして解析し、直接表示しません。

しかし端末が OSC 3008 を認識しない場合、または中間層が制御文字をフィルタ、エスケープ、分断した場合、元の内容がそのまま表示されることがあります。よくある環境は次のとおりです。

  1. 古い端末エミュレーター。
  2. OSC 3008 に未対応の SSH クライアント。
  3. Web 踏み台やブラウザー端末。Apache Guacamole 環境など。
  4. Emacs term.el、シリアル端末、minicom など OSC 対応が限定的な環境。
  5. 多段転送で中間層が制御シーケンスを壊す場合。

このとき、次のような内容が表示されることがあります。

1
]3008;start=...;type=command;cwd=...

また、machineid=bootid=pid=comm=cwd= を含む長い文字列が見えることもあります。これは通常のプログラム出力ではなく、本来は端末が処理するべきコンテキスト信号です。

OSC 3008 かどうかを判断する方法

まず 3 つの手がかりを見ます。

第一に、文字化けの中に 3008;start= または 3008;end= があるか。

第二に、コマンド実行の前後、特に Enter でコマンドを実行するたびに出るか。

第三に、現在のシステムが systemd の OSC 関数を読み込んでいるか。

1
declare -f __systemd_osc_context_precmdline

関数定義が表示されれば、現在の shell に関連ロジックが読み込まれています。

profile ファイルも確認できます。

1
ls -l /etc/profile.d/80-systemd-osc-context.sh

このパスが存在し、かつ端末が OSC 3008 に対応していない場合、文字化けの原因はここである可能性が高いです。

一時的な回避策

現在のセッションだけで見た目を直したい場合は、関連関数を上書きして PS0 を空にできます。

1
2
3
4
__systemd_osc_context_precmdline() { :; }
__systemd_osc_context_common() { :; }
__systemd_osc_context_escape() { :; }
PS0=""

SSH セッションだけで自動処理したい場合は、~/.bashrc に入れます。

1
2
3
4
5
6
7
8
if [[ -n "$SSH_CLIENT" || -n "$SSH_TTY" || -n "$SSH_CONNECTION" ]]; then
    if declare -f __systemd_osc_context_precmdline >/dev/null; then
        __systemd_osc_context_precmdline() { :; }
        __systemd_osc_context_common() { :; }
        __systemd_osc_context_escape() { :; }
        PS0=""
    fi
fi

この方法は影響範囲が小さく、システム全体の profile ファイルを変更せず、後で削除するのも簡単です。

システム全体で無効化する方法

このマシンの端末環境が全体的に OSC 3008 と相性が悪いと確認できている場合は、システム全体で無効化することも考えられます。ただし、すべてのユーザーとログインセッションに影響するため慎重に行ってください。

systemd スクリプトのコメントでは、/etc/profile.d/80-systemd-osc-context.sh のシンボリックリンクを削除し、対応する tmpfiles 断片を mask して再作成を防ぐという考え方が示されています。

次のようなコマンドを参考にできます。

1
2
3
test -h /etc/profile.d/80-systemd-osc-context.sh && \
rm -v /etc/profile.d/80-systemd-osc-context.sh && \
ln -s /dev/null /etc/tmpfiles.d/20-systemd-osc-context.conf

これは ~/.bashrc だけを変更するより重い対応です。まず一時回避策で問題が OSC 3008 によるものか確認してから、システム全体で無効化するか判断してください。

個人の SSH クライアントだけが非対応なら、いきなりシステム設定を変えるのはおすすめしません。自分の ~/.bashrc を変更するか、SSH クライアントのログイン後コマンドでクリーンアップするほうが安全です。

端末をアップグレードするか、OSC 3008 を無効化するか

使っている端末がすでに OSC 3008 に対応しているなら、この機能は残すほうがよいです。将来的にリモート shell、コンテナ、仮想マシン、権限昇格コマンドのコンテキスト表示をよりわかりやすくしてくれる可能性があります。

一方、実際の環境が踏み台、シリアル、古い端末、Web SSH で、短期的にクライアントを更新できないなら、抑止するほうが現実的です。多くの運用場面では、拡張コンテキスト表示よりも、きれいで読める端末出力のほうが重要です。

次の順番で考えるとよいです。

  1. 端末エミュレーターをアップグレードできるなら、まずアップグレードする。
  2. アップグレードできず、自分だけに影響するなら、~/.bashrc またはクライアントのログイン後コマンドで抑止する。
  3. マシン全体が影響を受けるなら、/etc/profile.d/80-systemd-osc-context.sh を削除し、tmpfiles 断片を mask することを検討する。

短いまとめ

OSC 3008 はマルウェアでも、通常のプログラムが勝手に出している文字列でもありません。UAPI.15 で定義された端末コンテキスト信号であり、shell、SSH、コンテナ、仮想マシン、権限昇格コマンドの階層関係を端末に理解させることが目的です。

問題は多くの場合、互換性です。対応端末は自動的に解析します。非対応端末では、制御シーケンスが文字化けとして表示されることがあります。

3008;start=machineid=bootid=cwd= のような内容が見えたら、まず systemd の 80-systemd-osc-context.sh によって注入されているか確認してください。そのうえで、影響範囲に応じて一時回避、個人の .bashrc での抑止、またはシステム全体での無効化を選びます。

记录并分享
Hugo で構築されています。
テーマ StackJimmy によって設計されています。