Ubuntu Weekly Recipe

第666回Waypipeを用いてWayland環境で「X転送 over SSH」っぽいことをしてみる

先日リリースされたUbuntu 21.04では、長年の懸案だった「Waylandセッションのデフォルト化」が再度実施されました。今後2022年4月にリリースされる予定のUbuntu 22.04 LTSに向けて、さまざまな問題を洗い出し、対応を行っていくことになります。今回はそんなWayland環境で「X転送 over SSH」っぽいことを実現してみましょう。

Waylandセッションになったということ

Waylandとは2008年に登場したの次世代ディスプレイサーバーです。もともとは、X.Orgの開発者でもあったKristian Høgsbergが最小のディスプレイサーバーとウィンドウコンポジッターを組み合わせたものとして個人的に作成していたソフトウェアでした。その後、X.Orgの開発者たちがX Window System/X.Orgを置き換える次世代のディスプレイサーバーとして開発に参加するようになったのです[1]⁠。

WaylandはX Window Systemの正当な後継という位置づけであるため、Xを採用している各OSがWaylandへ移行することは既定路線でした。問題は「いつ移行するのか」だったのです。

メジャーなLinuxディストリビューションにおいて先陣を切ったのは、アップストリームの開発者が多く参加しているFedoraです。2014年頃にはすでにGNOMEセッションでWaylandを選択できるようになり、2016年のリリースにおいてWaylandがデフォルトになっていたようです。ただしKDEセッションでWaylandが使われるようになったのは先日リリースされたFedora 34からでした。

Ubuntuは2017年の17.10で一度Waylandをデフォルトセッションに採用したものの、いくつかの機能が足りないために次のLTSである18.04ではXセッションに戻しています。その後、数年の時をかけてようやくWaylandセッションのデフォルト化に踏み切ったというわけです。その間に、GNOMEを採用するメジャーなディストリビューションのほとんどはWaylandセッションのデフォルト化に踏み切っています。つまりUbuntuはどちらかというと後発な対応となりました[2]⁠。

さて、第663回のUbuntu 21.04の主な変更点でも解説されているように、Waylandへの移行の最大の障害のひとつが画面共有機能がないことでした。つまりリモートデスクトップの機能がなかったのです。Waylandはその設計方針から、Wayland自身はレンダリングAPIを提供せず、またそのAPIはネットワーク透過ではありません。つまりリモートデスクトップを実現するためには、Xに実装されていたいくつかの機能をレンダリングサーバーで肩代わりする必要がありました。

たとえばGNOMEの場合、WaylandコンポジッターでもあるMutterがレンダリングを、マルチメディアのネットワーク越しのパイプラインを実現できるPipeWireが画面情報等の転送を、一般的なデスクトップ環境における各種APIをフレームワーク化したxdg-desktop-portalが諸々のUI部分に対するバックエンドとして動きます。つまりXの頃と比べると複雑化しているのです。

画面共有に関わるさまざまなツールが協調して取り込まれないといけなかったため、UbuntuのWayland対応は遅れることになりました。ちなみに、実際は現時点でもアプリケーションによってはWayland上での画面共有に対応していません[3]⁠。

WaylandセッションでアプリをSSH越しに転送する

まずはUbuntu 21.04がインストールされたマシンを2台用意してください。次のようにどちらもWaylandが動いていることを確認します。

$ echo $XDG_SESSION_TYPE
wayland

リモート側にSSHサーバーをインストールして、クライアントからウィンドウをX転送してみましょう。

リモート$ sudo apt install openssh-server

通常であれば、次のように実行すると-XオプションでX転送が有効化され、ウィンドウが表示されるはずです。

クライアント$ ssh -X リモートサーバー gedit

おそらく何も反応がないでしょう。たとえばリモート側ですでにGUIにログインしている状態なら、画面を確認してみてください。なぜかgeditが起動してしまっているはずです。

試しにgeditではなくてFirefoxを起動してみるとどうでしょうか。

クライアント$ ssh -X リモートサーバー firefox

こちらは起動しましたね。実はUbuntuのWaylandセッションでは「Xwayland」も一緒に起動するようになっています。これは何かと言うとWaylandコンポジッターと連携できるXサーバーです。つまり純粋にX Window Systemしか対応していないソフトウェアはXwaylandを従来のXサーバーとして利用することで、Wayland環境でも起動できるようになっています。

FirefoxはWayland/X Window Systemを起動時に判定して適切に処理しています。このため、従来のSSH越しのX転送でも動いていたというわけです。

それに対してgeditは、Wayland決め打ちです[4]⁠。SSH越しのX転送を利用しようとしても正しく処理できませんでした。今回はこのgeditをWaypipeというソフトウェアを使ってSSH越しに起動してみます。

Waypipeのビルド

Waypipeはざっくり言うと「Waylandクライアントのプロキシー」です。ソケット越しに共有メモリーを構築することで、リモートのアプリケーションがメモリー上に描画した内容を、ローカルのWaylandコンポジッターが描画できます。

描画用のメモリーはSSH越しに共有できるため、SSHと同程度に経路はセキュアであり、ネットワーク上で必要な設定はSSH通信できるようにするだけです。Waypipeそのものの詳細な仕組みはGoogle Summer of Codeで開発を行った際のブログを参考にすると良いでしょう。

残念ながらまだDebianパッケージ化されていないため、かんたんにはインストールできません[5]⁠。そもそも3月にリリースされた0.8.0より前まではREADME.mdにも「still somewhat unstable」と書かれていたぐらいなので、まだまだきちんと動かないところはありそうです。よって今回はPPA等も用意せずに、最新のコードをビルドするところから始めます。

今回のターゲットはUbuntu 21.04です。20.04でも動くとは思いますが、Waylandセッション等がきちんと動くかをまず確認しておいてください。ビルドはリモート・ローカルいずれのマシンでもかまいません、作られるのはシングルバイナリなので、リモートとローカルが同じリリースのUbuntuなら片方でビルドして、もう片方にバイナリをコピーするという手も使えます[6]⁠。

まずは必要なパッケージをインストールします。

$ sudo apt install meson python3 pkg-config \
    ninja-build build-essential \
    liblz4-dev libgbm-dev libdrm-dev libavcodec-dev \
    libva-dev libswscale-dev

前半の2行は必須のパッケージです。後半の2行は動画の再生や効率的な転送を求めるなら必要なライブラリです。とりあえずすべてインストールしておくと良いでしょう。

あとはソースコードをクローンして、meson/ninjaでビルド&インストールするだけです。今回はホームディレクトリーの中の~/bin以下にインストールすることにします。

$ git clone git@gitlab.freedesktop.org:mstoeckl/waypipe.git
$ cd waypipe
$ mkdir ~/bin build-waypipe
$ meson --buildtype debugoptimized -Dprefix=$HOME/bin build-waypipe
$ ninja -C build-waypipe install

これで~/bin/bin/waypipeがインストールされました。これをリモートとローカル両方のマシンに~/bin/waypipeとしてコピーしてください。

さらに、前述のようにすべてインストール済みなら、リモートとローカルの両方に次のパッケージをインストールします。

$ sudo apt install libavcodec58 libavutil56 libswscale5 libva2 ffmpeg

このときインストールするパッケージは、Waypipeのビルド時にインストールしたライブラリパッケージの状態に依存します。なおパッケージの末尾の数字はUbuntuのリリースによって異なることもあるため、タブ補完等を利用して適切な数字を入力してください。

あとは実際にwaypipeコマンドを実行してみます。

$ ~/bin/waypipe -h
Usage: waypipe [options] mode ...
A proxy for Wayland protocol applications.
Example: waypipe ssh user@server weston-terminal

Modes:
  ssh [...]    Wrap an ssh invocation to run waypipe on both ends of the
                 connection, and automatically forward Wayland applications.
  server CMD   Run remotely to invoke CMD and forward application data through
                 a socket to a matching 'waypipe client' instance.
  client       Run locally to create a Unix socket to which 'waypipe server'
                 instances can connect.
  recon C T    Reconnect a 'waypipe server' instance. Writes the new Unix
                 socket path T to the control pipe C.
  bench B      Given a connection bandwidth B in MB/sec, estimate the best
                 compression level used to send data

Options:
  -c, --compress C     choose compression method: lz4[=#], zstd=[=#], none
  -d, --debug          print debug messages
  -h, --help           display this help and exit
  -n, --no-gpu         disable protocols which would use GPU resources
  -o, --oneshot        only permit one connected application
  -s, --socket S       set the socket path to either create or connect to:
                         server default: /tmp/waypipe-server.sock
                         client default: /tmp/waypipe-client.sock
                         ssh: sets the prefix for the socket path
      --version        print waypipe version and exit
      --allow-tiled    allow gpu buffers (DMABUFs) with format modifiers
      --control C      server,ssh: set control pipe to reconnect server
      --display D      server,ssh: the Wayland display name or path
      --drm-node R     set the local render node. default: /dev/dri/renderD128
      --remote-node R  ssh: set the remote render node path
      --remote-bin R   ssh: set the remote waypipe binary. default: waypipe
      --login-shell    server: if server CMD is empty, run a login shell
      --threads T      set thread pool size, default=hardware threads/2
      --unlink-socket  server: unlink the socket that waypipe connects to
      --video[=V]      compress certain linear dmabufs only with a video codec
                         V is list of options: sw,hw,bpf=1.2e5,h264,vp9

無事に実行できたのなら準備が整いました。

Waypipeでアプリケーションのウィンドウを転送してみる

まずは試しに先ほど起動できなかったgeditを実行してみます。Waypipeは「モード」で挙動をコントロールできます。一番よく使うのがsshモードです。これはリモートとクライアントの間でSSHを経由してメモリー領域を共有しつつ、リモートのアプリケーションをローカルでレンダリングするモードとなります。つまりSSHで言うところのX転送です。

$ ~/bin/waypipe --remote-bin ~/bin/waypipe ssh リモートサーバー gedit

--remote-bin ~/bin/waypipeはwaypipeコマンドのパスを指定しています。もしsshでログインした先のPATHから見える位置にwaypipeコマンドがいるなら、指定は不要です。

図1 もちろん日本語入力も可能だし候補ウィンドウも表示される
図1

無事に起動できたようです。たとえば「システムモニター」のネットワークを表示しながらいろいろ動かしてみて、どれくらい使用するか確認すると良いでしょう。

さらに動画も再生可能です。試しにGoogle ChromeでYouTubeを再生してみましょう。Google ChromeはそのままだとXwaylandを利用して起動してしまいます。よって次のようにいくつかオプションを与えて起動してください。

Xを使うためにうまく動かないパターン:
$ ~/bin/waypipe --remote-bin ~/bin/waypipe ssh リモートサーバー google-chrome
[547884:547884:0508/210909.707057:ERROR:browser_main_loop.cc(1386)] Unable to open X display.

Waylandで起動するパターン:
$ ~/bin/waypipe --remote-bin ~/bin/waypipe ssh リモートサーバー google-chrome \
    --enable-features=UseOzonePlatform --ozone-platform=wayland
図2 このようにリモートのブラウザーで動画を再生可能
図2

ただし音声は転送されません。もし音声も転送したいのなら、別途PulseAudioの設定など、Waypipeとは別の仕組みが必要になります。

このようにWaypipeを使えば、X転送と同じような使い方でリモートのGUIアプリケーションをリモートで起動しつつ、ローカルのディスプレイに描画が可能です。通信はすべてSSHを経由するため、ほとんどのケースにおいて「別途ポートを開けるよう管理者に依頼する」必要もないはずです。もし手持ちの環境がすべてWaylandに移行するようなら、一度試してみてはいかがでしょうか。

おすすめ記事

記事・ニュース一覧