PureBuilder Simplyのアップデート (ReST完全対応)

3ヶ月ぶりとなったPureBuilder Simplyのアップデート。

今回はMimir Yokohamaのウェブサイトの新連載(そもそも連載開始にお金かかるので時期未定)でReSTructured Textを使うため、ReSTに完全対応した。

完全対応のポイントは、従来きちんと対応できていなかったdocinfo(Bibliographic Elements)に対応するようにした。

ただし、これは「対応した」という言い方が適切なのかどうかわからない。 ひとつは、自力でdocinfoを解釈する部分が間違っていたのと、仕様の理解自体正しくなかったので適正にした。

File.open([dir, filename].join("/")) do |f|
    l = f.gets
-        if l =~ /:[A-Za-z]+: .*/ #docinfo
-          docinfo_lines = [l.chomp]
+        if l =~ /:([A-Za-z]+): (.*)/ #docinfo
+          frontmatter = { $1 => [$2.chomp] }
+          last_key = $1

        # Read docinfo
        while(l = f.gets)
            break if l =~ /^\s*$/ # End of docinfo
-            if l =~ /^\s+- / && (docinfo_lines.last.kind_of?(Array) || docinfo_lines.last =~ /^:.*?: +-/) # List items
-              if docinfo_lines.last.kind_of?(String)
-                docinfo_lines.last =~ /^:(.*?): +- *(.*)/
-                docinfo_lines[-1] = [ [$1, $2] ]
-              end
-              docinfo_lines.last[1].push(l.sub(/^\s+- +/).chomp)
-            elsif l =~ /^\s+/ # Continuous line
-              docinfo_lines.last << " " + $'.chomp
-            elsif l =~ /^:.*?: +.*/
-              docinfo_lines.push l.chomp
+            if l =~ /^\s+/ # Continuous line
+              docinfo_lines.last.push($'.chomp)
+            elsif l =~ /:([A-Za-z]+): (.*)/
+              frontmatter[$1] = [$2.chomp]
+              last_key = $1
            end
        end

-          # Convert Hash.
-          frontmatter = {}
-          docinfo_lines.each do |i|
-            if i.kind_of?(Array) #list
-              # Array element
-              frontmatter[i[0]] = i[1]
-            elsif i =~ /^:author: .*[,;]/ #author
-              # It work only pandoc style author (not Authors.)
-              author = i.sub(/:author: /, "")
-              if author.include?(";")
-                author = author.split(/ *; */)
-              elsif author.include?(",")
-                author = author.split(/ *, */)
-              end
+          # Treat docinfo lines
+          frontmatter.each do |k,v|
+            v = v.join(" ")
+            if((k == "author" || k == "authors") && v.include?(";")) # Multiple authors.
+              v = v.split(/\s*;\s*/)

-              frontmatter["author"] = author
-            elsif i =~ /^:(.*?): +(\d{4}-\d{2}-\d{2}[T ]\d{2}[0-9: T+-]*)$/ #datetime
-              key = $1
-              time = DateTime.parse($2)
-              frontmatter[key] = time
-            elsif i =~ /^:(.*?): +(\d{4}-\d{2}-\d{2}) *$/ #date
-              key = $1
-              time = Date.parse($2)
-              frontmatter[key] = time
-            elsif i =~ /^:(.*?): +/
-              key = $1
-              value = $'
-              frontmatter[key] = value
+            elsif k == "date" # Date?
+              # Datetime?
+              if v =~ /[0-2][0-9]:[0-6][0-9]/
+                v = DateTime.parse(v)
+              else
+                v = Date.parse(v)
+              end
+            else # Simple String.
+              nil # keep v
            end
+
+            frontmatter[k] = v
        end

    elsif l && l.chomp == ".." #YAML
        # Load ReST YAML that document begins comment and block is yaml.
+          @extra_meta_format = true # ReST + YAML is not supported by Pandoc.
        lines = []

        while(l = f.gets)

考え方自体に大きな変更があったため、変更行数も多い。 ただし、22165f2よりも8f6e453のほうが以前のバージョンとの違いは少なくなっている。

ここでひとつポイントだ。ReSTの仕様上、authorsa,b,cと書かれた場合は3人の著者になる。a,b,c;と書かれた場合は1人の著者になる。 だが、Pandocは;でのみ分割するので、この仕様に従っている。このため非常にシンプルな仕様だ。

基本的にdocinfoの場合、authorsdateのみが特別扱いされる。 Pandoc的にもそのような仕様になっており、authorsauthorの代わりに書ける点も正式な仕様に従っている。 PureBuilderもこれにならって、それ以外のフィールドについては特別扱いしない。 また、Pandocは仕様にない項目を入れてもエラーにしないため、この点も合わせてある。

この上で、メタデータの解釈はPandocに委ねることにした。 従来は-Mオプションを付加して上書きしていたのだが、解釈の違いからバグにもなったし、Pandocのほうが優秀なのでこのような挙動は取りやめた。

ただし、PureBuilder的にはこれではちょっと困る。 サイトの内容に関する情報をメタデータに記述する風習があるため、メタデータに書ける内容が決められてしまうのは困るのだ。

そこで従来サポートされていた「ReSTでも先頭をコメントにした場合はそのあとYAMLメタデータを書くものとする」という仕様も維持している。 この場合、Pandocは解釈できないため、@extra_meta_formatというインスタンス変数を追加し、これが真の場合のみ-Mオプションでのメタデータを使うことにした。

こうしてReSTも完全対応できることとなった。 asciidocも結構人気があるらしいのだけど、Pandocが入力にasciidocをサポートしていないのでサポートすることはできない。 Textileをサポートしてほしい人がいれば対応は考えなくもないけれど、私が知る限りPureBuilderでTextileを使いたい人はいないはずだ。1


  1. 比較的複雑な構造を書けることがPureBuilderの利点であり、また簡易な記述をしたい人にとってもMarkdownが困ることはないはずだからだ。Textileにはコメント機能があるため、サポートすること自体は不可能ではない。

2018年版コミュニケーションメディア 所感

eメール

最近はeメールを使う人は本当に減ってしまったようで、個人的なやりとりに使われることは現状ほぼない。

特段見くびるほどの欠点があるわけではないと思うのだが、実際のところレガシーな使いづらさというのは存在する これはどちらかというとソフトウェア実装上の問題である。

ただし、柔軟性に関しては最も優れている。 オープンで共通の仕様という意味では被験するのはXMPPくらいのもので、 この点を理由にeメールを扱いたい理由はある。

若い人の場合、大学生、あるいは大学卒であればeメールは扱えるが、 そうでない場合は扱えない傾向があるようだ。 また、eメールを扱う場合でも、特定のアプリとアカウントが紐付けられた状態(LINEのような状態)で認識しており、使い方は理解できていないというケースが目立ち始めている。

LINE

依然として日本においてデファクトスタンダードの座を譲らないLINEである。

アプリの使いづらさはある程度改善されつつあるのだが、Windowsアプリ版の使いにくさには批判が多いようだ。 また、アプリの種類によって機能が異なる点は単なる使いづらさになっている。

モバイルアプリ版に関してはヴォイスメッセージ機能が秀逸である。 トーク画面でヴォイスボタンを押している間に発した言葉が送信される。 あまり活用されていないようだが、非常に優れた機能といっていいだろう。(LINEが初なのかはわからないが)

メッセージ消去機能がついたが、Skypeと比べれば証拠消しなどには使いづらく良心的だと言えるだろう。 直前消去だけで良いのではないかという気がするのだが…

通話品質はあまり改善がみられず、非常に切れやすい。

LINEが優れている点はわずかだが、普及とスタンプという蓄積要素があるため、乗り換えも容易ではないところまできたように感じる。

Kakao Talk

混沌の時代に現れ、様々な雑な対応でユーザーを失ったカカオトークだが、 その混沌と荒野、そして雑さをついて出会い系利用に拍車がかかった。

直接的にIDを交換するケースもあるが、他の出会い系メディアからの連絡手段交換として利用されているようだ。 出会い系、というとソフトにきこえるが、ほぼ売春・援助交際目的である。

そして、それ以外の用途というのはあまり利用されていないことから、 「インストールすることでその人の素行品性が問われる」という状況にあるといっていい。

Skype

Microsoftに買収されてから改悪を続けるSkype。 5.4になってからレビューを著しく落としたのは有名な話だが、それ以降さらなる改悪を続けている。

まず、コンタクト機能がなくなった。 電話帳利用を強制しており、利用しない場合はコンタクト機能が存在せず管理不能の状態になる。 チャットした相手は全て自動的にコンタクトがある状態になる、という仕組みだ。 電話知用機能のないLinuxの場合、「チャットをしたことのある相手一覧」という形式になる。

また、ステータス表示機能もなくなった。 これは一旦削除し、激しい批判を浴びて復活させたのだが、完全になくなってしまった。 これは今までは「モバイルアプリ版にはない」だったのだが、ステータス通知APIもろともの削除で、外部アプリを使った場合でもステータスは取得できない。

SkypeのSNS化進行も激しく、だいたいsnapchat化している。 Skypeのコンタクトにはあまり親しくない人も多いことから非常に困ってしまう。

通話品質も低下しつつあり、より通話品質に優れるアプリが登場していることから避けたいメディアになりつつあるようだ。

Telegram

LINEと類似の一通りの機能、無料のステッカー、ステッカー同様にGIFを利用する機能などなかなか魅力的だ。 また、LINEに追加されたヴォイスメッセージに近い機能(もうちょっと慎重)と、同様に利用できるビデオメッセージも利用可能だ。

だが、致命的なのはWhats Appを意識しているのか、「電話番号と本名を前提としている」ということだ。

基本的にユーザー検索時はファーストネームと電話番号が必須である。 裏技として、ユーザーIDだけわかっていればシークレットチャット開始→ID検索ということも可能だが、 検索結果に本名と電話番号が表示される。

検索を防ぐ設定はないので女の人などは非常に不安だろうし、 「電話の代わり」という仕様はWhats App同様いかがなものか。 「日本人は秘密主義すぎる」みたいに言われることがあるのだが、世界的に見てそんなに受け入れられている気はしない。 むしろアメリカ人が個人情報やプライバシーに無頓着すぎると思う。

通話機能はかなり切れやすく、快適ではない。

XMPP

依然としてXEP-180(ビデオ通話)に対応したクライアントがなく、どのアプリも快適にはほど遠い。

期待されていたJitsiに関してはそもそもXMPPをやめてしまったという状況だ。

モバイルアプリは最も有力なのはXabberだが、Xabberにしてもあまりデキはよくない。 重要な相手とのやりとりには使用できないが、あまり密ではない(取りこぼしがあったとしても構わない)ような相手とのやりとりには利用できるだろう。

「XMPPを利用している人は少ない」という前提のもと、「XMPPを利用できないレベルの人にXMPPでやりとりをできるようにする」という作業を行う状況というのが限定的すぎて現実的ではない。

ただし、メリットは全くないわけではない。 XMPPアカウントの取得は比較的容易で、XMPPのウェブクライアントも存在するためだ。

PCの場合はインストールも不要で簡単にXMPPで会話することが可能になる。

現実的にはスマートフォンでインストールもせずにチャットするのはかなり難しい。 だが、そのような人はおそらく会話する意思に著しく乏しいものと思われるので配慮するだけ無駄ではないか。

Jitsi Meet

Jitsi MeetはWebRTCクライアント+WebRTCサービスである。

一応、他のWebRTCサービスも利用できるようだ。

つながっている手段が通話機能を提供していない場合(たとえばTwitterなど)、あるいはその方法が適切ない場合に有効である。 特に個人情報を教えたり登録したりする必要もないのもメリットと言えるだろう。

Discord

Discrodが良いものであることは既に明らかだが、明確な欠点がある。 それは、アカウントの使い分けが困難だということだ。

PCならいくつか方法はあるのだが、Discordは活用ジャンルが非常に広く、通知を分けることもできず、特定のチャンネルに集中することも難しい。 別にDiscordに固有の問題ではないとはいえ、便利だからといってあれもこれも教えていると困ることになってしまう。

方法は色々ある(たとえばInvisibleにするとか)のだが、使い方に配慮が必要なのは確かだ。

通話品質は非常によく、現状ベストな選択肢である。

なお、招待すると「ゲーマー」のところにみんなひっかかるので、そろそろ他のことも書いてくれないか。

Slack

日本語版がリリースされ、CMも打たれたため、名前だけは知っているような状態になっている。

実際一般にそこまでSlackが広まった印象はないが、少なくとも提案したときに受ける抵抗は減ったように思う。

Slackはログを商売にしているが、あまり良い手ではないというのが一般の意見だ。 XMPPサポートをそれを理由に切ったことも批判的に見られている。

Slackは全体的にDiscordに見劣りするが、明確にDiscordより優位な点がある。 それは切り分けが容易なことである。ユーザー単位では認識されないし、チームは分離される。 常に適切なリージョンで分割できるというのはメリットといっていいだろう。

なお、Slack Webはスマートフォンでは利用できない。

Google Hangouts

品質はあまり変わっておらず、少なくともSkypeやLINEよりは良い選択肢といっていいと思う。

しかし、Googleアカウントが特定されてしまうこと、結果としてGMailもわかってしまうこと、Google+をやっている場合は自動的にGoogle+でもつながってしまうことなどFacebook Messanger同様の使いづらさが存在している。

Viber

楽天が取り込んでいるけれども、プライバシー面で改善されたという話は聞かず、 かなり不安な状況のままだ。

まとめ

  • Discordは良いもの
  • 仕事など限定された環境のつながりはDiscordでなくSlackで
  • DiscordとSlackの使い分けは大事
  • LINEやTelegramのようなプライバシーに関わるものをプライベートなつながりでないのに要求する人からは逃げよう
  • 親しい人とLINEを交換するのは妥当。ただし「その人が親しい人でなくなるリスク」とセットで考えないと、ブロック/削除しても可視性が残る
  • Googleアカウントでつながってしまっている人に関してはHangoutsというのも結構妥当
  • 「とりあえずXMPPアカウント交換しておこうかな」というのもまぁ妥当な判断
  • カカオトークはやめましょう。人間性の問題になる。
  • Telegramは現状、電話番号以上のプライバシーの塊なので、本当に親しい人とでないと困難
  • チャット機能に関してはTelegramかなり使い心地いい
  • 妥当というか無難なのはLINE。メッセージ到達性は一番優秀
  • メール侮るべからず
  • 匿名性を保ってつながりたいという人は、XMPPとJitsi Meetを提案するのが多分無難
  • そのうちチャットつくるよ
  • Mimir YokohamaはTelegramが対応済み。XMPPもそのうち対応します

先のシェルスクリプトを形にしました

一時キーボード無効

Temporary Disable Keyboard @GitHub

Xinputを使用してデバイスを無効/有効にするためのもの。

主な変更点は次の通り

  • 無効化を「時間制」「ダイアログ」「無限」から選択できるようにした
  • Zenityダイアログを若干調整
  • ランチャー用の.desktopファイルを追加

Zshスクリプトだが、case内で非常に珍しい;&(フォールスルーする)を使用している。

ターミナルエミュレータ選択機能

Terminal Selector @GitHub

主な変更点は次の通り

  • ランチャー用の.desktopファイルを追加
  • KDE Service用の.desktopファイルを追加
  • Nemo用の.nemo_actionファイルを追加
  • 対応する端末を大幅に増加
  • 利用できない端末を選択肢から除外するように変更
  • Zenityダイアログを調整
  • 柔軟に端末を追加できるようにファイルマネージャでの起動用の引数対応が連想配列で使用できるように変更

連想配列を使用するための方式は次のようなものだ。

わざわざ連想配列をテンプレート文字列とし、そこから配列に変換している理由は

  • 連想配列に格納できるのは文字列のみ
  • 置き換えしてからではDIRがIFSを含む可能性がある

【Linux複合技】 SSHポートフォワーディングしてセッションを維持しつつ多段SSHでファイル転送

SSHでリモートからポートフォワーディング

例えばNAT内に存在するコンピュータに対してサーバーに代理応答してもらう方法になる。

この方法を使えばNAT内のコンピュータに対してサーバーを経由してアクセスすることが可能になる。

このポートフォワーディングは以下のように行う。

-Lと比べ-Rはその意味を見失いやすい。

5000はリモートホスト側のポートであり、リモートホストのこのポートにアクセスすることによりローカルホスト側にアクセスすることができるようになる。

hostはローカルホストからみたポートだ。 sshを実行しているコンピュータ自身に転送するのであればlocalhostだし、LANの他のコンピュータ、例えばbobにアクセスしたいのならbobになる。

10000はhostのポートである。 ここではSSHに対してログインさせたいので、変更していなければ22になるだろう。

リモート側ポートはそのポートを開くため、1024より小さい値を指定するには特権がいる。 逆にホスト側ポートは単にそのポートに転送するだけなので転送したいポートを指定する。

このままだとログインしてしまうのでバックグラウンドで実行するオプション-fと、何もコマンドを実行しない-Nを組み合わせるのが一般的だ。

なお、-gをつけない限りはリモートホストではループバックネットワークインターフェイスにのみバインドされるため、リモートホストに対して外部からアクセス可能になるわけではない。

セキュリティを考えれば、公開はせずにSSHでサーバーにログインし、そこから転送するようにしたほうがいいだろう。

接続を維持する

このままでは環境によっては入出力がないSSHセッションはすぐ切断されてしまう。 そこで、このSSHセッションは維持してもらいたい。

ピンポンのための双方向入出力

結局使わなかったアイディア。

通常のシェルスクリプトではあるプロセスに対して別のプロセスが読むことも書くこともする、ということはできない。 そういうことがしたい場合の方法は主にふたつ。

Procfs

/proc/<PID>/fd/0に対して書き込めば標準入力に入力が与えられる。

このとき注意すべきは、標準入力がつながっているのが端末だと端末に書いてしまうので、標準入力はパイプにつながっている必要がある。 特にパイプから何も入れる予定がないのであればsleepにつなぐと良いだろう。

出力はパイプで受け取れば良い。

FIFO

こっちのほうが普通。 FIFOを使えばそこに書かれた出力を一括して受け取れる。

複数のプロセスが書く場合はちゃんと排他制御すること。 また、後処理を忘れないこと。

リモートがぽん

こんな感じでよかった。

pong.sh

readの-tオプションでタイムアウトしている。Zshスクリプト。

30秒ごとにPINGしていて、45秒間PONGが返ってこなかったら、たぶんコネクションは死んでいる。 まぁ、多分ぴんぽんする必要はないけど。(片方が送り続けていればいいはず)

ただ、死活チェックのために返してほしい。 どちらかといえば向こう側に送り続けてもらう必要がある。

コネクションが切れたら確実に死んでもらおう

こんな感じ。

もしくはこんな感じ。

Systemdでrespawn

systemdで起動させることにして、死んだら復活させてもらう。

[Unit]
Description=Connect for SSH port forwarding

[Service]
ExecStart=/home/jrh/bin/sshforward.zsh
ExecStop=/bin/kill -TERM $MAINPID
Restart=always

enableする予定はないので、Installは省略。

プロセスが死んだらRestart=alwaysなので復活する。 停止するときはユニットをstopすること。

ちなみに、KillModeを省略しているので、停止時にはsshもkillされる。

SSHの設定

SSHログインできるようにする

ここらへんは基本手順。

まず鍵の生成

$ ssh-keygen -f ~/.ssh/server_rsa

これを何らかの方法でサーバーの~/.ssh/authorized_keysに追記する。 ない場合は作成。パーミッションは600であること。

続いて~/.ssh/configに設定

Host server
  Uesr jrh
  Port 22
  HostName server.example.com
  IdentityFile ~/.ssh/server_rsa

これで簡単にログインできるようになった。

$ ssh server

こちらはログインする側の端末の設定である。

コマンド専用鍵を作る

まずは前項と同じように鍵を作って登録する。

転送は許可しないといけないので、こんな感じ。

authorized_keysのコマンド用鍵の行の先頭に以下のようなフィールドを追加する。

command="/usr/local/bin/pong.sh",no-pty,no-X11-forwarding

今回はno-port-forwardingしてしまうと動作しなくなる。 ポートフォワーディングと、標準入出力を使ったやりとりを行うためだ。

なお、この時SSH鍵を使用してアクセスした場合 コマンドは入れなくて良い。 そのコマンドしか実行できないので、勝手にそのコマンドが実行される。

なお、configファイルにはRemoteForwardの項目を入れるようにすると良いだろう。 次のように。

Host proxy-server
  User jrh
  Port 22
  HostName server.example.com
  RemoteForward 10000 localhost:22
  IdentityFile ~/.ssh/server-proxy_rsa

こちらはログインされる側の端末の設定。

多段ログイン

ログインする端末はサーバーにログインしたあと、SSHポートフォワーディングを利用してログインされる端末にログインする。 ログインする端末から見るとSSHを二度行うことになる。

もちろん、このようなことはできないわけではないのだが、これだとSCPやSFTPなどは利用しづらい。 また、できればコマンド一発で簡単にログインしたいところだ。

そこで、まずはログインする側の鍵をログインされる側に登録する。 これでまず、サーバーを経由せず直接に鍵認証可能な状態になる。

その上で設定ファイル(~/.ssh/config)にProxyCommandとしてサーバーのSSHを経由して接続する設定を記述する。

Host target-proxy
  user jrh
  Port 10000
  HostName localhost
  IdentityFile ~/.ssh/proxylogin_rsa
  ProxyCommand ssh -CW %h:%p server
  • Port-Rによってサーバーに開かれているポート
  • HostNameはサーバーにログインしてから接続するものなので、localhost
  • IdentityFileは直接のログインにも使用できるログインされる側に登録されているもの
  • ProxyCommandとして先程の設定に記載したHostの値を利用する

これで外出中でもサーバーを経由して端末にログイン可能になった。

複雑なので手順のまとめ

本文は知識順に記述しているが、ここではミニマムな達成順で記述する。

ここではログインする側の端末をlaptop、ログインされる側の端末をdesktop、サーバーをserverと呼称する。

  1. desktopで鍵を生成し、serverauthorized_keysに登録する
  2. laptopで鍵を生成し、serverauthorized_keysに登録する
  3. laptopで鍵を生成し、desktopauthorized_keysに登録する
  4. serverで動作するPONGコマンドを作成する
  5. server上のdesktopの鍵をPONGコマンドに結びつける。
  6. desktop上でserverに接続するためのコマンドを作成する。このコマンドは基本的に <PING> | <SSH> | <READ>
  7. desktopで作成したコマンドを反復起動するためのsystemdユーザーユニットを作成する
  8. desktop~/.ssh/configにコマンドに結びつけられたserverにログインする設定を記述する。ポートフォワーディングも記述する
  9. laptop~/.ssh/configserverへのログイン、及びdesktopに対してProxyCommandserverを通じてログインする設定を記述する

これで準備は完了。あとはdesktopでユニットを起動し、laptopからserver経由desktopログインのsshを実行するだけ。

おわりに

SSHの応用, systemdユーザーユニット, シェルスクリプト, 入出力とファイルデスクリプタ, procfs, FIFO, プロセスとシグナルなど一般デスクトップユーザーは触れずにいるような基礎知識が詰まったものになり、計らずもさながら中級Linuxer認定試験のような内容になった。

基礎に関する知識と理解があればこのように便利に利用することもできるので、 この記事がmagicalに見える方はぜひがんばって取り組んでいただきたいと思う。

【検索ワードに応えて】 ThinkPad X1 Carbon (2017, シルバー) のお話, Linux関連, その他

久しぶりの検索ワード反応企画。 ThinkPad X1 Carbon関連の検索が多かったので、併用されたワードに従ってコメントさせていただくことにする。

なお、ThinkPad X1 CarbonについてはYouTube (はるかみ☆ チャンネル)で開封動画を掲載させていただいているので、よかったらそちらもどうぞ。

また、そのほかLinux関連の検索にも回答させていただく。

ThinkPad X1関連

基本的なレビュー

ラップトップをガリガリ使うモバイラーなら持たない理由が見つからない。

XPS13のような13.3インチの中でも小型のものを除けばボディは13.3インチと変わりないサイズだ。 しかし14インチの画面は明らかに見やすく、作業もしやすいし、人に画面を見せるときにも良い。

そして非常に軽量でバッテリーマイレージも素晴らしい。 もちろん、キーボードはあらゆる現行ラップトップの中で最高だと思う。

唯一欠点としては天板の外板が弱い。 既に凹み、割れがある。それほど目立たないし、動作には問題ないのだが、ちょっとカナシイ。

買い時について

「高性能」「最先端」などを意識したい場合はリリース間もない頃が良い。

リリース直後は割引はなく、およそ半年程度で30%程度の割引になる。モデル末期は40%を越える程度の割引だ。

基本的にはThinkPadの場合30%程度割り引いた状態で競争力のある通常価格で、割引のない最初期というのはフリーク向けと考えられる。 なので買い時を気にするような人なら直後はないだろう。

春頃には30%前後の割引になっていたりするから(現時点でサイト公式のクーポンが28%、恐らくメルマガや店頭クーポンならもう少し行くだろう)、それぐらいが買い時ではなかろうか。

少しでも安く買いたい人は、翌年モデルが発表されてから決めるといい。 その頃にはかなり安くなっているし、在庫が残っていれば翌年モデルが出てからでも安く買える。 翌年モデルを待つべきかどうかの判断も出来てお得だ。

2017と2018に関しては、X1 Carbonとしての進化は微々たるものだったけれど、第8世代プロセッサになったという点がとても大きい。 ただ、私はかなり安く買えたので、そのタイミングでよかったと思う。

シルバーのThinkPadについて

良いと思う。

ThinkPadらしくないという批判はあるだろうけれども、マットだけれども黒のピーチスキンよりもサラッとした手触りで汚れもつきにくく目立たない。

シルバーのラップトップはそれなりに存在するので目立たないということは言えるけれども、「Hacker’s itemたるThinkPad」と「美しくおしゃれな高級ラップトップ」を両立させる意図は十分に達成できている。

控え目にいっても最高にかっこいい。

国産と中国産について

何も違わないから安心してほしい。 400万円するようなP920でも中国産だ。

なお、納期はかかる。X1は予定納期よりも遅かったという人が珍しくないようなので注意してほしい。

その他の検索ワード回答

シェルスクリプトの並列実行

基本的な形式は次のとおり

シェルスクリプトの並列実行は待ち合わせず投げっぱなしにするのが基本。

ただし、どうしても待ち合わたい場合はwaitを使う方法もある。

とした時、$!でサブシェルのPIDが取れるので、

としておけば待ち合わせる必要があるタイミングで

とすることができる。これはBashでもZshでも共通。

入出力をやりとりするのであれば、下流のパイプに流すべきで、親プロセスが出力を披露ようなことは考えないほうがよい。

どうしてもであればファイルに書いてwaitするか、もしくはZshならProccess Substitutionを使用する。

あるいは大量の処理があり、複数のワーカーを走らせたい場合はflockを使うのが無難だろう。 例えば以下のようにする。

要点は以下の通りだ。

  • 関数workerが各ワーカーが実行する内容
  • これをサブシェル内でworker nの形で実行する
  • ファイルデスクリプタはクローンされるので、標準入力から読んだ場合、親シェルもサブシェルも同じものを読み進めるし、位置も同時に変化する
  • しかし、もし同時に読んでしまうとおかしなことになるので、読んでいる間は他のシェルに読んでもらっては困る
  • そこでロックファイルを作り、これをロックすることで排他制御する。いわゆるドットロック。
  • exec 9>| .lockでファイルデスクリプタ9番をロック用に確保
  • 開きっぱなしになるので、flockでこのファイルをロック
  • ロックを獲得できたら標準入力から読む
  • 読み終わったらファイルデスクリプタを閉じてロックも解放
  • これでキューから1行読めたので、処理を進める
  • 「1行では足りないよ!」という場合、エントリをファイルに書いておいてディレクトリにまとめ、キューはファイル名にするとかすれば良い

AMD APU (Godavari/Kaveri) と Linux

特に問題はない。

以前はCatalystドライバのおかげでずいぶん苦労させられたけれども、 AMDGPUになって以来目立った問題は発生していない。

割と電気を食うけれど性能は微妙なので嬉しくはないと思う。 コストパフォーマンス的にみれば、これくらいの性能がおいしいという人は多いと思う。 ビデオ関連も充実しているし、Killerシリーズなんかは機能も充実しているしね。

XMPP

ちょっと話題が広すぎて何を求めているのかがわからない。ごめんよ。

DPI

これかなり広い。

LinuxでのHi-DPIの話なら/etc/X11/xorg.conf.d/以下にモニターの設定ファイルを書く。 40-monitor.confとかで。

Section "Monitor"
    Identifier             "<default monitor>"
    DisplaySize            286 179    # In millimeters
EndSection

で、KDE Plasmaを使うといい感じになる。 Plasmaを使わない場合については、Qtアプリケーションについてはなんとかなるけれど、GTKに関してはうまいことスケールしない。

LightDMに関してはGreeterの設定ファイルにxft-dpi=と書きましょう。

Hi-DPIはLinuxでは結構苦手にしている感じ。

フォントの設定はまた別。

某人物について

コメントする気はありません。

VP9のハードウェアエンコード

  • Intel QSVを使ってください
  • LinuxならVA-API経由ffmpegが良いよ
  • ただし画質は絶望的だったとは言っておく

Intel QSV * H.265

  • LinuxならVA-API経由ffmpeg
  • 速度はそこそこ。X.265とは雲泥の差。CPU負荷は0ではないというか、普通に40%くらいはいく
  • ちゃんとVA-APIドライバ入れようね

scale_vaapi

VA-APIを使用する場合の出力画面サイズ指定。 変更しない場合は省略して大丈夫。

AMD VCE / NVIDIA NVENC

VCEはLinux的にはVA-API経由。なのでQSVと一緒。

ただし、AMDのビデオドライバはVA-APIだけじゃなくVDPAUも使える。 ところがVA-APIがエンコード/デコードなのに対してVDPAUはデコードのみ。

VA-APIをVDPAUに転送するドライバ(libva-vdpau-driver)と、VDPAUをVA-APIに転送するドライバ(libvdpau-va-gl)があり、 VDPAUを有効にして、VA-APIに転送するドライバを使ってしまうと(Nvidiaの場合はVA-APIが使えないのでこうする)デコードのみになってしまって使えない。

ちゃんとlibva-mesa-driverを使いましょう。

Nvidiaの場合はVA-APIをサポートしておらず、VDPAUはエンコードができないので、 NVENCは専用のインターフェイスになっている。

どっちが優れているというのは難しいけれど、Nvidiaのほうが対応フォーマットは多い。

DiscordとSlack

基本的にはDiscordがいいと思うし、最近はSlackも微妙だと思うのだけれども、「両方あると嬉しい」という面もある。

Discordの場合「ユーザーという概念がある」ということが大きい。

Discrodでつながると、どのようなつながりであれ、「その人」というのが見えてしまう。 Discordは通知をカスタマイズできないため、「通知をオンにしている全ての人からの通知が一律に行われる」ということになる。 棲み分けをする方法がない。

これはLINEと同様の問題である。 恋人だろうが、ゲーム仲間だろうが、仕事の人間だろうが、区別する方法がないのだ。 アカウントの切り替えは難しいためアカウントで分けるのも現実的ではないし、アカウントで認識されているから一時的な連絡先にもしにくい。

Slackの場合はやりとりもつながりもあくまでそのチーム内でのことなので、仕事の関係などであればSlackのほうが便利だ。

自前メールサーバーからのメールをGMailが受け取らない

主にはホスト認証の関係。

GMailは送られてきたメールサーバーが、本当にそのメールを送る資格があるかをチェックする。

設定方法はいくつかあるけれど、SPFレコードを書くのが楽。 「メール SPF」で検索すると色々出てくる。

LinuxとRealTek ネットワークカード (RTL8152ほか)

ASIXよりはマシだけれど安定して苦しめてくれるRealTekのネットワークカード。

Linuxではネットワークアダプタは

Intel > Atheros (Killer) > Broadcom > RealTek > Asix

だからね。

RealTekのWiFiモジュールであるRTL8152のドライバはカーネルモジュールとしてある。 Arch Linuxの場合はDKMSになっていて、AURからインストール可能。 場合によってはRTL8153をブラックリストに入れる必要がある。

インストールしなくても動作はするけれど、うまく交信できなくなったりする。 安定性の面から言えば入れるべきだけれど、入れたところでうまくいかない場合もある。

「うちのディストリビューションにはないよ!!」という方。 Archにおいで。むしろManjaroにおいで。

ffmpeg * V4L2 と Logicool C270 で HD 30FPS 撮りたい

まずffmpeg * V4L2 でウェブカム撮影する方法は次の通り。

デバイスは適宜読み替えること。

NVENCでG.265に変換して変換して保存する場合は

QSVやVCEでVA-APIを使う場合は

さらにPulseを使って音も入れる場合は

HEVC+Opus MKVが最近のお気に入り。

そしてビデオサイズを指定する場合は

しかしLogicool C270だとこれで5FPSになってしまう。かっくかくやでぇ。 そこでフレームレートを指定する。

けれどこれでも7.5FPSになってしまう。 v4l2-ctlを用いて確認してみる。

YUVU以外にMotionJPEGもサポートしている、と。 で、詳しく見てみると…

% v4l2-ctl --list-formats-ext
ioctl: VIDIOC_ENUM_FMT
        Index       : 0
        Type        : Video Capture
        Pixel Format: 'YUYV'
        Name        : YUYV 4:2:2
                Size: Discrete 640x480
                        Interval: Discrete 0.033s (30.000 fps)
                        Interval: Discrete 0.040s (25.000 fps)
                        Interval: Discrete 0.050s (20.000 fps)
                        Interval: Discrete 0.067s (15.000 fps)
                        Interval: Discrete 0.100s (10.000 fps)
                        Interval: Discrete 0.200s (5.000 fps)
                Size: Discrete 160x120
                        Interval: Discrete 0.033s (30.000 fps)
                        Interval: Discrete 0.040s (25.000 fps)
                        Interval: Discrete 0.050s (20.000 fps)
                        Interval: Discrete 0.067s (15.000 fps)
                        Interval: Discrete 0.100s (10.000 fps)
                        Interval: Discrete 0.200s (5.000 fps)
                Size: Discrete 176x144
                        Interval: Discrete 0.033s (30.000 fps)
                        Interval: Discrete 0.040s (25.000 fps)
                        Interval: Discrete 0.050s (20.000 fps)
                        Interval: Discrete 0.067s (15.000 fps)
                        Interval: Discrete 0.100s (10.000 fps)
                        Interval: Discrete 0.200s (5.000 fps)
                Size: Discrete 320x176
                        Interval: Discrete 0.033s (30.000 fps)
                        Interval: Discrete 0.040s (25.000 fps)
                        Interval: Discrete 0.050s (20.000 fps)
                        Interval: Discrete 0.067s (15.000 fps)
                        Interval: Discrete 0.100s (10.000 fps)
                        Interval: Discrete 0.200s (5.000 fps)
                Size: Discrete 320x240
                        Interval: Discrete 0.033s (30.000 fps)
                        Interval: Discrete 0.040s (25.000 fps)
                        Interval: Discrete 0.050s (20.000 fps)
                        Interval: Discrete 0.067s (15.000 fps)
                        Interval: Discrete 0.100s (10.000 fps)
                        Interval: Discrete 0.200s (5.000 fps)
                Size: Discrete 352x288
                        Interval: Discrete 0.033s (30.000 fps)
                        Interval: Discrete 0.040s (25.000 fps)
                        Interval: Discrete 0.050s (20.000 fps)
                        Interval: Discrete 0.067s (15.000 fps)
                        Interval: Discrete 0.100s (10.000 fps)
                        Interval: Discrete 0.200s (5.000 fps)
                Size: Discrete 432x240
                        Interval: Discrete 0.033s (30.000 fps)
                        Interval: Discrete 0.040s (25.000 fps)
                        Interval: Discrete 0.050s (20.000 fps)
                        Interval: Discrete 0.067s (15.000 fps)
                        Interval: Discrete 0.100s (10.000 fps)
                        Interval: Discrete 0.200s (5.000 fps)
                Size: Discrete 544x288
                        Interval: Discrete 0.033s (30.000 fps)
                        Interval: Discrete 0.040s (25.000 fps)
                        Interval: Discrete 0.050s (20.000 fps)
                        Interval: Discrete 0.067s (15.000 fps)
                        Interval: Discrete 0.100s (10.000 fps)
                        Interval: Discrete 0.200s (5.000 fps)
                Size: Discrete 640x360
                        Interval: Discrete 0.033s (30.000 fps)
                        Interval: Discrete 0.040s (25.000 fps)
                        Interval: Discrete 0.050s (20.000 fps)
                        Interval: Discrete 0.067s (15.000 fps)
                        Interval: Discrete 0.100s (10.000 fps)
                        Interval: Discrete 0.200s (5.000 fps)
                Size: Discrete 752x416
                        Interval: Discrete 0.040s (25.000 fps)
                        Interval: Discrete 0.050s (20.000 fps)
                        Interval: Discrete 0.067s (15.000 fps)
                        Interval: Discrete 0.100s (10.000 fps)
                        Interval: Discrete 0.200s (5.000 fps)
                Size: Discrete 800x448
                        Interval: Discrete 0.050s (20.000 fps)
                        Interval: Discrete 0.067s (15.000 fps)
                        Interval: Discrete 0.100s (10.000 fps)
                        Interval: Discrete 0.200s (5.000 fps)
                Size: Discrete 800x600
                        Interval: Discrete 0.050s (20.000 fps)
                        Interval: Discrete 0.067s (15.000 fps)
                        Interval: Discrete 0.100s (10.000 fps)
                        Interval: Discrete 0.200s (5.000 fps)
                Size: Discrete 864x480
                        Interval: Discrete 0.050s (20.000 fps)
                        Interval: Discrete 0.067s (15.000 fps)
                        Interval: Discrete 0.100s (10.000 fps)
                        Interval: Discrete 0.200s (5.000 fps)
                Size: Discrete 960x544
                        Interval: Discrete 0.067s (15.000 fps)
                        Interval: Discrete 0.100s (10.000 fps)
                        Interval: Discrete 0.200s (5.000 fps)
                Size: Discrete 960x720
                        Interval: Discrete 0.100s (10.000 fps)
                        Interval: Discrete 0.200s (5.000 fps)
                Size: Discrete 1024x576
                        Interval: Discrete 0.100s (10.000 fps)
                        Interval: Discrete 0.200s (5.000 fps)
                Size: Discrete 1184x656
                        Interval: Discrete 0.100s (10.000 fps)
                        Interval: Discrete 0.200s (5.000 fps)
                Size: Discrete 1280x720
                        Interval: Discrete 0.133s (7.500 fps)
                        Interval: Discrete 0.200s (5.000 fps)
                Size: Discrete 1280x960
                        Interval: Discrete 0.133s (7.500 fps)
                        Interval: Discrete 0.200s (5.000 fps)

        Index       : 1
        Type        : Video Capture
        Pixel Format: 'MJPG' (compressed)
        Name        : Motion-JPEG
                Size: Discrete 640x480
                        Interval: Discrete 0.033s (30.000 fps)
                        Interval: Discrete 0.040s (25.000 fps)
                        Interval: Discrete 0.050s (20.000 fps)
                        Interval: Discrete 0.067s (15.000 fps)
                        Interval: Discrete 0.100s (10.000 fps)
                        Interval: Discrete 0.200s (5.000 fps)
                Size: Discrete 160x120
                        Interval: Discrete 0.033s (30.000 fps)
                        Interval: Discrete 0.040s (25.000 fps)
                        Interval: Discrete 0.050s (20.000 fps)
                        Interval: Discrete 0.067s (15.000 fps)
                        Interval: Discrete 0.100s (10.000 fps)
                        Interval: Discrete 0.200s (5.000 fps)
                Size: Discrete 176x144
                        Interval: Discrete 0.033s (30.000 fps)
                        Interval: Discrete 0.040s (25.000 fps)
                        Interval: Discrete 0.050s (20.000 fps)
                        Interval: Discrete 0.067s (15.000 fps)
                        Interval: Discrete 0.100s (10.000 fps)
                        Interval: Discrete 0.200s (5.000 fps)
                Size: Discrete 320x176
                        Interval: Discrete 0.033s (30.000 fps)
                        Interval: Discrete 0.040s (25.000 fps)
                        Interval: Discrete 0.050s (20.000 fps)
                        Interval: Discrete 0.067s (15.000 fps)
                        Interval: Discrete 0.100s (10.000 fps)
                        Interval: Discrete 0.200s (5.000 fps)
                Size: Discrete 320x240
                        Interval: Discrete 0.033s (30.000 fps)
                        Interval: Discrete 0.040s (25.000 fps)
                        Interval: Discrete 0.050s (20.000 fps)
                        Interval: Discrete 0.067s (15.000 fps)
                        Interval: Discrete 0.100s (10.000 fps)
                        Interval: Discrete 0.200s (5.000 fps)
                Size: Discrete 352x288
                        Interval: Discrete 0.033s (30.000 fps)
                        Interval: Discrete 0.040s (25.000 fps)
                        Interval: Discrete 0.050s (20.000 fps)
                        Interval: Discrete 0.067s (15.000 fps)
                        Interval: Discrete 0.100s (10.000 fps)
                        Interval: Discrete 0.200s (5.000 fps)
                Size: Discrete 432x240
                        Interval: Discrete 0.033s (30.000 fps)
                        Interval: Discrete 0.040s (25.000 fps)
                        Interval: Discrete 0.050s (20.000 fps)
                        Interval: Discrete 0.067s (15.000 fps)
                        Interval: Discrete 0.100s (10.000 fps)
                        Interval: Discrete 0.200s (5.000 fps)
                Size: Discrete 544x288
                        Interval: Discrete 0.033s (30.000 fps)
                        Interval: Discrete 0.040s (25.000 fps)
                        Interval: Discrete 0.050s (20.000 fps)
                        Interval: Discrete 0.067s (15.000 fps)
                        Interval: Discrete 0.100s (10.000 fps)
                        Interval: Discrete 0.200s (5.000 fps)
                Size: Discrete 640x360
                        Interval: Discrete 0.033s (30.000 fps)
                        Interval: Discrete 0.040s (25.000 fps)
                        Interval: Discrete 0.050s (20.000 fps)
                        Interval: Discrete 0.067s (15.000 fps)
                        Interval: Discrete 0.100s (10.000 fps)
                        Interval: Discrete 0.200s (5.000 fps)
                Size: Discrete 752x416
                        Interval: Discrete 0.033s (30.000 fps)
                        Interval: Discrete 0.040s (25.000 fps)
                        Interval: Discrete 0.050s (20.000 fps)
                        Interval: Discrete 0.067s (15.000 fps)
                        Interval: Discrete 0.100s (10.000 fps)
                        Interval: Discrete 0.200s (5.000 fps)
                Size: Discrete 800x448
                        Interval: Discrete 0.033s (30.000 fps)
                        Interval: Discrete 0.040s (25.000 fps)
                        Interval: Discrete 0.050s (20.000 fps)
                        Interval: Discrete 0.067s (15.000 fps)
                        Interval: Discrete 0.100s (10.000 fps)
                        Interval: Discrete 0.200s (5.000 fps)
                Size: Discrete 800x600
                        Interval: Discrete 0.033s (30.000 fps)
                        Interval: Discrete 0.040s (25.000 fps)
                        Interval: Discrete 0.050s (20.000 fps)
                        Interval: Discrete 0.067s (15.000 fps)
                        Interval: Discrete 0.100s (10.000 fps)
                        Interval: Discrete 0.200s (5.000 fps)
                Size: Discrete 864x480
                        Interval: Discrete 0.033s (30.000 fps)
                        Interval: Discrete 0.040s (25.000 fps)
                        Interval: Discrete 0.050s (20.000 fps)
                        Interval: Discrete 0.067s (15.000 fps)
                        Interval: Discrete 0.100s (10.000 fps)
                        Interval: Discrete 0.200s (5.000 fps)
                Size: Discrete 960x544
                        Interval: Discrete 0.033s (30.000 fps)
                        Interval: Discrete 0.040s (25.000 fps)
                        Interval: Discrete 0.050s (20.000 fps)
                        Interval: Discrete 0.067s (15.000 fps)
                        Interval: Discrete 0.100s (10.000 fps)
                        Interval: Discrete 0.200s (5.000 fps)
                Size: Discrete 960x720
                        Interval: Discrete 0.033s (30.000 fps)
                        Interval: Discrete 0.040s (25.000 fps)
                        Interval: Discrete 0.050s (20.000 fps)
                        Interval: Discrete 0.067s (15.000 fps)
                        Interval: Discrete 0.100s (10.000 fps)
                        Interval: Discrete 0.200s (5.000 fps)
                Size: Discrete 1024x576
                        Interval: Discrete 0.033s (30.000 fps)
                        Interval: Discrete 0.040s (25.000 fps)
                        Interval: Discrete 0.050s (20.000 fps)
                        Interval: Discrete 0.067s (15.000 fps)
                        Interval: Discrete 0.100s (10.000 fps)
                        Interval: Discrete 0.200s (5.000 fps)
                Size: Discrete 1184x656
                        Interval: Discrete 0.033s (30.000 fps)
                        Interval: Discrete 0.040s (25.000 fps)
                        Interval: Discrete 0.050s (20.000 fps)
                        Interval: Discrete 0.067s (15.000 fps)
                        Interval: Discrete 0.100s (10.000 fps)
                        Interval: Discrete 0.200s (5.000 fps)
                Size: Discrete 1280x720
                        Interval: Discrete 0.033s (30.000 fps)
                        Interval: Discrete 0.040s (25.000 fps)
                        Interval: Discrete 0.050s (20.000 fps)
                        Interval: Discrete 0.067s (15.000 fps)
                        Interval: Discrete 0.100s (10.000 fps)
                        Interval: Discrete 0.200s (5.000 fps)
                Size: Discrete 1280x960
                        Interval: Discrete 0.033s (30.000 fps)
                        Interval: Discrete 0.040s (25.000 fps)
                        Interval: Discrete 0.050s (20.000 fps)
                        Interval: Discrete 0.067s (15.000 fps)
                        Interval: Discrete 0.100s (10.000 fps)
                        Interval: Discrete 0.200s (5.000 fps)

YUVUだと720Pは5/7.5FPSしか選択肢がない。 一方、MotionJPEGであれば30FPSでもとれるらしい。 720Pのカメラ、とあるのだが、何気に1280×960などというフォーマットもあるのは、センサー自体が4:3だからということだろうか。

というわけでmotionJPEGを指定する。

しかし、大量のエラーメッセージが出る

[video4linux2,v4l2 @ 0x5600641ec760] Thread message queue blocking; consider raising the thread_queue_size option (current value: 8)
[mjpeg @ 0x5600641eea00] unable to decode APP fields: Invalid data found when processing input

しかも、画質は非常に悪い。 確かにHDで30FPSを取るという目的は達成できたけども、画質を考えると普通に640×480で取るのと大差ないという悲しさよ。

MotionJPEGで高画素で取るのなら一旦はMotionJPEGのままでもよさそう。

調べた限り、USB3.0接続のウェブカムというのはなさそうで、 H.264でFHD 30fpsの出るウェブカムというのはあるにはあるけれども、UVC1.5に対応していないとなかなか面倒なことになる…と

手頃にH.264が取れるウェブカムとしては、ELPのカメラモジュールがあったりするのだけれど、 ウェブカムに色々求めてはいけないということなのだろうか…

Linuxでキーボードをつないだまま掃除したい

キーボードが汚れているなぁ、と気づいたとき、あるいはキーボードがべたつくとき、キーボードを掃除したいけれど電源を切りたくないということはないだろうか。 特にデスクトップの後方につないでいる場合や、ラップトップの場合にはキーボードを外すのも面倒な話だ。 スリープにしてもキーボードに触ったら復帰してしまうし、なかなか厄介だ。

そこで一時的にキーボードをつないだまま無効にする方法を考えてみた。

キーボードを無効にする方法としてはXによるデバイス認識であればxinputが利用できる。

次の方法で認識されているデバイスを確認する。

あまり見やすくはない。

⎡ Virtual core pointer                      id=2    [master pointer  (3)]
⎜   ↳ Virtual core XTEST pointer                id=4    [slave  pointer  (2)]
⎜   ↳ ELECOM ELECOM UltimateLaser Mouse         id=9    [slave  pointer  (2)]
⎣ Virtual core keyboard                     id=3    [master keyboard (2)]
    ↳ Virtual core XTEST keyboard               id=5    [slave  keyboard (3)]
    ↳ Power Button                              id=6    [slave  keyboard (3)]
    ↳ Power Button                              id=7    [slave  keyboard (3)]
    ↳ FCL USB Keyboard                          id=8    [slave  keyboard (3)]

キーボード全体をオフにしてしまうと困るかもしれない。 なので、Virtual core keyboardをオフにすることは思いとどまったほうがいいだろう。

FCL USB Keyboard(idは8)を無効にするには次のようにする。(手前側の8がID、後のほうは固定の値)

有効に戻すにはこうだ。

外付けキーボードが他に用意できるならいきなり無効にしても構わないが、そうできない場合は時間で復帰するようにしたほうがいいだろう。

安全のためアプレットから復帰できるようにするほうがいいだろう。 マウスで操作できるよう、Zenityを使ってon/offできるようにした。 また、off時にはタイマーをかけることもでき、タイマーをかけない場合は確認される。

Gistで公開している。 Zshスクリプトであり、実際にZshの機能を使用しているのでBashでは動かない。 Zenityはなかなかコマンド置換が難しく、(f)は欠かせない。

昨今のブラウザ事情

ウェブブラウザとウェブテクノロジー

今やウェブブラウザは中核的ソフトウェアといって過言ではないだろう。 世界はウェブを中心に回っているのが現実だ。

だが、それは問題をふたつもたらしている。

ひとつは、過当競争だ。

あまりにもウェブに依存しすぎているがために、ウェブが高度技術化し、開発コストも増大しとてもついていくのが困難になっている。 現状ウェブ開発をリードしているのはGoogleであり、ChromiumによるBlink/V8エンジンが最も進んでいる状態だ。 これに対抗するのはMozillaのGecko/SpiderMonkeyで、Chromiumよりも進んだ実装を行うこともないではないものの、現実にはFirefoxに実装されChromiumに実装されない機能は大概標準化されないのが現実だ。 (ただし、標準化においてはある程度Mozillaが主導権を握る展開が続いている)

MicrosoftによるEdgeHTMLはこれと比べて明確に遅れをとっている。 これはちょっとした理由がある。 そもそもEdgeHTMLはWebKitをターゲットとしているのだが、WebKitは現代ではとてもではないが使い物にならない。

WebKitを開発しているのはAppleだし、そもそもWebKitはSafariのものなのだが、WebKit自体はオープンソースだし、他のソフトウェアも利用することができる。 とはいえqtwebkitに関しては既に廃止となっており、WebKitのバージョンも古い。

WebKitとBlinkを比べる良い手段はMidoriとFalkonを使用することだろう。 MidoriはAppleがcommitするWebKitGtk+を使用しており、一方Falkon(かつてのQupzilla)はBlinkを使用するQtWebEngineを使用している。 そして、Falkonで表示に困ることはないのだが、Midoriでは普通に「表示できないサイト」や「利用できないサイト」が存在するのだ。

根本的にWebKitはカジュアルでライトなウェブブラウジングに的を絞っていると考えて良い。 つまり、ちょっとした調べ物などであり、完全に表示できることよりも軽快であることを望むためのものであり、実際Safariは機能的にかなり限定的である。 最小限の機能を搭載することからも「ちょっとした行為」であり、目を尖らせてに向き合うものではないと考えているのがわかる。

実際、Microsoft Edgeもそのような考え方であり、機能的に貧弱だったInternet Explorerと比べても機能はさらに削られており、メニューは実に寂しい。

生殺与奪を握られたウェブエンジン

だが、この問題はなかなか複雑だ。

AppleとGoogleはこれらウェブエンジンを「自由に使わせなければならない」呪縛にとらわれている。

これはもともと、Appleが既にある程度成熟していたウェブエンジンに参入するにあたり、スクラッチから開発することをよしとせず、KHTML/KJS Softwareをベースにすることを選択したことある。 当時のKHTMLはLGPLだったので、ここから派生するためにはLGPLを継承せざるをえなかった。 もちろん、そのWebKitから派生したBlinkにしても同様にLGPLを継承するしかない。これにより望む望まざるに関わらず「開発したものは配布するためには公開し自由を保証しなければならない」ということになったのだ。

そのため均衡が保たれているのだが、実際技術標準というか、「ウェブの世界」そのものは主にはGoogle、そしてAppleの意向は完全に反映されるのであり、 世界をウェブが支配している以上世界の支配者として君臨する状況を許してしまっている。

これは、GoogleやAppleが「採用する」と決めた方針に反する方法がないという問題だ。 LGPLである以上、ウェブブラウザを「秘密」にしてしまったり、伏せられた邪悪な機能を搭載するのは難しい。 だが、正式にプライバシーを侵害するような機能、仕様を採用すると決定したとして(既にそのようなものがなくもないが)、それを覆すことができない。 もちろん、フォークするという選択肢はあるが、ウェブブラウザの規模と進歩を考えるとGoogle並の開発力で追従するということは現実的ではない。 結局のところ誰もがGoogle, Mozilla, Apple, Microsoftの少なくともいずれかには従わなければならないという状況にあるのだ。

実際、Chromeユーザーでも「Google及びChromeに対する信頼度」はかなり低いというアンケート結果がある。

唯一の良心といえるのがMozillaだが、Mozillaの体制や行動がプライバシー上の理由で批判されたことは一度や二度ではない。

結果的に我々の生殺与奪は彼らに握られているというのが現実であり、 そして現状においてはそれを覆すほどのリソースはどこにもないのだ。 これはOperaのような老舗ブラウザ屋がBlinkを採用し、WebKitの祖先であるKDE ProjectがQtWebEngineを採用していることからも明らかだろう。

セキュリティとプライバシー

もうひとつの問題はウェブを侵食するプライバシーの問題だ。

セキュリティについて昔と比べることは意味がない。

昔はセキュリティを脅かすものは見るからに怪しげな「悪人」であり、「怪しげなものを遠ざければ良い」というものだった。 また、そのようなセキュリティ上の問題は「プライバシーの問題」ではなかったのだ。

ところが現代においてはサービスを利用する上でプライバシーを侵害されることは前提であり、 それどころかウェブブラウザを利用することによってもプライバシーが侵害されるという状況にある。

なぜこのような状況になるのか。

まず、Facebookを筆頭にして、現在はプライバシーが主要な商品となりすぎていて、 どこもかしこもプライバシーを欲しがるという現状がある。 これはウェブの技術如何に依らず根本的な問題である。 「プライバシーを侵害したいと思う人がいなければプライバシーを侵害するものが作られることはない」のだから。

だが、彼らウェブブラウザデベロッパー達はこれらの「プライバシーを侵害したい」という要求に応えているし、 また彼ら自身がプライバシーの侵害を望んでもいる。GoogleやAppleが過剰かつ積極的に個人情報を収集している状況を忘れてはいけない。

我々は日々普通に使っているだけでも著しくプライバシーを侵害され続けている状況にあるのだ。

プライバシーを取り返せ

中には完全にプライバシーを保護するブラウザもなくはないが、代償は大きい。 そうではなく、普通は求めているのは「与えて良いと判断して選択し許可した以外のプライバシー侵害は認めない」というものだろう。

だが、これはなかなか難しい。 ウェブブラウザの根本的な機能として既にプライバシー侵害を可能にする機能が組み込まれているからだ。 例えばComodo Dragonはプライバシーを重視しているということだが(PrivDog事件もあったが)、トラッキングなどは普通に行えてしまう。

また、Vivaldiもプライバシーを重視しているが、それでも問題になっているプライバシーはあまり解決しない。 裏側で悪意ある行動をとっていないというだけで、知られないうちに何かが行われることを防いでくれるわけではないからだ。

ある程度、そして確実に効果がある方法は「セッションを残さない」ことだ。 トラッキングが最も驚異なのは、「アクティビティが同定でき」「蓄積されるから」だ。 蓄積されなければ同一人物なのか否かを特定することは困難になるし、取得可能な状況はある程度限定的になる。

Chromiumならば次のようにすれば最初からプライベートモードになるし、使うたびに情報はクリアされ、ある程度安心だ。

chromium --incognito

もちろん、これでTwitterとGoogleとFacebookに同時にログインしたら台無しである。 そんなことはせず、他のサービスを利用するときは必ずブラウザを閉じて行うべきだ。 これは、ログインしなくても、サイトごとにセッションを切るべきである

Vivaldiも--incognitoオプションを受け付けるため、Vivaldiを使えばさらに(いくらか)安全になる。 Linuxにおいてはデフォルトブラウザとして--incognitoつきでVivaldiを起動するようにしてしまうといい。 以下は私が使用している$HOME/.local/share/applications/incogvivaldi.desktopファイルだ。

[Desktop Entry]
Version=1.0
Name=Vivaldi Incognito
# Only KDE 4 seems to use GenericName, so we reuse the KDE strings.
GenericName=Web Browser (with Incongnito)
GenericName[ja]=ウェブブラウザ (with Incongnito)
Comment=Access the Internet (with safety)
Comment[ja]=インターネットに安全にアクセス
Exec=/usr/bin/vivaldi-stable --incognito %U
Terminal=false
Icon=vivaldi
Type=Application
Categories=Network;WebBrowser;
MimeType=text/html;text/xml;application/xhtml_xml;image/webp;x-scheme-handler/http;x-scheme-handler/https;x-scheme-handler/ftp;
Actions=new-private-window;

[Desktop Action new-private-window]
Name=New Incognito Window
Name[ja]=新しいシークレット ウィンドウ
Exec=/usr/bin/vivaldi-stable --incognito

デフォルトの.desktopファイルとの違いは以下の通り

  • 起動時に--incognitoオプションを付加
  • 日本語と英語のみにし、翻訳を排除(どうせ読めないし、ローカル設定だし)
  • new-windowアクションを除去し、デスクトップの右クリックメニューもIncognito限定に

これで開くアプリケーションの選択肢としてVivaldi Incongnitoが追加される。

プライバシーを強化する設定やプラグインを追加すればより万全だろう。

いちいちセッションを切るのは面倒だ、というまっとうな意見のために、私はMy Browser Chooserというソフトウェアを開発している。 サービスごとにプロファイルをわけてしまえば、サービス側がプロファイルを横断しての情報収集はできない。 運用ポリシーさえ守ればだいぶ楽になる。Zshスクリプトなので、Unix的な環境においてのみ機能する。

危ういスマートフォンブラウザのプライバシー

パソコンなら(特にLinuxならば)このようにいくらかの解決策を提示できる。 だが、本当に厄介なのはスマートフォンだ。

スマートフォンの場合ブラウザが握ることができる情報はパソコンの比ではない。 だが、にもかかわらずユーザーに与えられるコントロールは極めて少ない。

スマートフォン的な設計思想として、「可能な限りコントロール部品を減らす」というものがある。 例えばMicrosoft Edgeはメニューボタンがハンバーガーメニューになっておりドロワー式だ。これはタッチデバイス向けの設計であり、GNOME3もまた同様の設計になっている。 ドロワーに含まれるメニューも少なく、ここまでメニューを減らしているのは当然に相応に機能自体を削っているということである。 機能が多ければそれだけなんらかの煩雑さを持たざるをえないからだ。

このような設計にしている理由はふたつある。

ひとつはスマートフォンの狭い画面での煩雑性が操作性の低下に直結するためだ。 そして、もうひとつはユーザーが馬鹿でいられるようにである。

私はこのような考え方は好きではない。 なぜパソコン用のソフトウェアが低機能なスマートフォン設計を採用せねばならないのか?

このコントロールの少なさがプライバシーの問題にも直結している。 現在では必須ともいえるプライベートモードがひどく軽視されているし、ページを開く時点でプライベートモードで開く選択肢を与えられない。 スマートフォンはパソコンよりもアプリからリンクを開く機会が多いにもかかわらずだ。

もちろん、プロファイルの切り替えもサポートされていない。 結果的にスマートフォンに蓄積された情報が簡単に抜き取られてしまう状況である。 なお、ブラウザを使い分けたところでAndroidならば多くのブラウザはAndroidのWebEngineを使用しているため、中身はひとつ、結局は使い分けに意味などない。 iOSについてはあまり知らないが、おそらくは似たようなものだろう。自前でのウェブエンジン実装はあまりに困難だからだ。

ひどい話だと思う。 だがAndroid WebEngineとはつまるところBlinkであり、なかなか太刀打ちできるものではない。

使い分けるのであれば、Mozilla FirefoxとMicrosoft Edge、そしてBlink系ブラウザ(Chrome, Operaなどだ)を使い分けるという方法だ。 数が少ないのであまりリスク軽減はできないが、いくらかマシな方法だろう。

元は表現力も機能も安定性もひどいものだったAndroid Firefoxだが、現在はかなり改善されており、十分実用になる。 Quantam(Firefox 57以降)をもてはやす記事ほど素晴らしいものだとは思わないが、「Firefoxが実用になる」しいうのは極めて喜ばしいことだ。

それよりも推奨できるのはFirefox Focusを使用することだ。 Firefox Focusは常時プライベートモードのFirefoxである。firefox --private-window相当ということではなく、プライバシー保護のためにかなりenhanceされている。 ほとんどのブラウジングにおいてはFirefoxで困ることはなく、またアクティビティを保存する必要もない。 そして、Mozillaによって提供されるFirefox FocusはCM Security Browseなどを使うよりもよっぽど信用できると言える。

そう、ブラウザは重大なプライバシー資源となっているために「信用できるか」が極めて重要なのだ。 ましてスマートフォンブラウザならなおさらで、プライバシーの塊であるスマートフォンを狙ったアプリなど数知れずある。 言ってしまえば大部分は怪しいし、よほど信用できない限りは使えないのが現実である。 ブラウザはその基本的な動作においてほぼ全権を要求するという点も大きい。正常に動作させるために権限を与える必要があるためにリスクが高いソフトウェアなのだ。

言い換えれば、悪意ある人からすれば「これはブラウザです」というふうに言っておけば

スマートフォンブラウザの機能にも不満

典型的なのはOperaで、Operaは以前はかなり高機能なブラウザであった。 PCブラウジングやページ保存、印刷機能も備えていたのだ。

ところがそれらの機能は現在はもうない。スマートフォンには必要ないと判断されたのだろう。 だが、実際はOperaはもはや必要な機能が削除されてしまって使い物にならない。 このような「スマートフォンだから」という言い訳は、一体スマートフォンだからなんだというのかと思うほどに理由になっていない。

表示の難しいinspectionなどはまだしも、印刷、ページの保存、文字エンコーディングの選択、プラグイン、ページ検索、 ローカルファイルの取り扱いなどがスマートフォンにおいて欠けている必要があるのだろうか? それ以外にもウェブブラウザが様々に豊富な機能を備えているにもかかわらず、スマートフォン版ではない、あるいはコントロールする手段が与えられていない。 なぜスマートフォンではユーザーからコントロールを剥奪するのか。理解し難い。

Vivaldiのようなブラウザをスマートフォンに最適化させたようなものは出ないのだろうか。 一応、Vivaldiのスマートフォンバージョンの開発は行われていはいるようだが。

Perffered application, Nemo, Dolphin, Thunarを設定してみた

Perffered application

Cinnamonで「お気に入りのアプリ」にできるのは.desktopファイルがあるものだけ。 ブラウザをプライベートモードで起動したかったのだが、なかったので作ってみた。

.dsktopファイルは基本的にはいわゆる.ini形式である。 XDGは兎にも角にもWindowsっぽい。

ユーザーローカルのファイルの場所は~/.local/share/applications

[Desktop Entry]
Version=1.0
Name=Vivaldi Incognito
GenericName=Web Browser (with Incongnito)
GenericName[ja]=ウェブブラウザ (with Incongnito)
Comment=Access the Internet (with safety)
Comment[ja]=インターネットに安全にアクセス
Exec=/usr/bin/vivaldi-stable --incognito %U
Terminal=false
Icon=vivaldi
Type=Application
Categories=Network;WebBrowser;
MimeType=text/html;text/xml;application/xhtml_xml;image/webp;x-scheme-handler/http;x-scheme-handler/https;x-scheme-handler/ftp;
Actions=new-private-window;

[Desktop Action new-private-window]
Name=New Incognito Window
Name[ja]=新しいシークレット ウィンドウ
Exec=/usr/bin/vivaldi-stable --incognito

/usr/share/applicaitions以下にたくさんあるので参考になる。

Cinnamon

Nemo の右クリックメニュー

右クリックメニューのコンテキストは~/.local/share/nemo/actions以下の/.nemo_actionファイル。 形式自体は.desktopファイルと同様だけれど、内容はちょっと違う。

[Nemo Action]
Name=Open in Konsole
Comment=Konsole

Exec=konsole --workdir "%P"

Icon-Name=konsole

Selection=none
Extensions=any;

これは「何も選択していない状態で右クリックすることでKonsoleで開く」もの。 %Pは現在のディレクトリ。 Selection=noneで非選択状態。 Extensionsは拡張子かMIMEタイプを指定する。ここでは何も選択しないのでany

[Nemo Action]
Name=Open with Thunar
Comment=Open with Thunar

Exec=thunar "%F"

Icon-Name=thunar

Selection=s
Extensions=dir;

こちらはディレクトリをThunarで開くメニュー。 Selectionsになっており、ひとつのファイルを選択した場合のみ。 Extensionsdirでディレクトリのみ。 %Fは選択ファイル

[Nemo Action]
Name=Open with Konqueror
Comment=Open with Konqueror

Exec=konqueror %F

Icon-Name=konqueror

Selection=notnone
Extensions=any;

Konqueror。 Selectionはひとつでも複数でも良いnotnone%Fをクォートしてしまうと、複数の場合に全部がひとつの引数にされてしまう。

Cinnamonのショートカットアプレット

カスタムはできるけれども、アイコンが特定のテーマのものを選択しなければいけなくなってしまう。

設定ファイルは.cinnamon/configs/panel-launchers@cinnamon.org/3.json。 ここで.desktopファイルを指定するのだが、ユーザーローカルなものは選択できない。 ユーザーローカルな.desktopファイルは~/.cinnamon/panel-launchersに配置しておく必要がある。 シンボリックリンク可。

KDE Plasma

Dolphinのカスタムアクション

場所は

で確認。~/.kde4以下に置いても多分駄目。

試しに作ってみた「Viewniorで開く」コマンドは以下の通り

[Desktop Entry]
Type=Service
ServiceTypes=KonqPopupMenu/Plugin
MimeType=image/png;image/jpeg;
Actions=setAsWallpaper

[Desktop Action setAsWallpaper]
Name=View With Viewnior
Icon=viewnior
Exec=viewnior %U

画像選択時に項目として登場する。

Dolphinのテンプレート

KDEはXDGのテンプレートを一切無視する。

使用されるテンプレートは以下の方法で探すことができる。

こちらも.desktopファイルで、URLとしてテンプレートになるファイルのアドレスを指定する必要がある。

[Desktop Entry]
Comment=Blog Markdown
Icon=text
Name=Blog Markdown
Type=Link
URL=/home/haruka/Templates/blogmd.md

XFce4

Thunarのカスタムアクション

.desktopファイルの話をすると思った?

残念でした。

“編集→アクションの設定” から設定することができる。

Thunarのテンプレートファイル

XDGフォルダ自体をシンボリックリンクにすると機能しない

ストレージングの再構成

AoEにしてみたり、複数ボリュームにしてみたり、内蔵で盛ってみたりと様々な手法で拡張を続けてきたストレージだが、ようやくNASを含む構成に変更された。 これにより片側最大192TBのボリュームを確保できるようになった。

NAS ReadyNAS528

NASはReadyNAS 528。

8ベイのNASである。 箱のサイズが随分大きくて驚いてしまったが、本体は8ベイとしては最小限のサイズになっている。

普通に構成するとRAID6になってしまい、選択の余地がない。 一旦ボリュームを削除してから再度作る必要がある。

この際RAID5を選択すると6台以上でのRAID5を推奨しないことを警告される。 この場合は、バックアップもされているので、RAID5で強行する。安心感で言えばRAID6のほうが良いと思う。

そしてiSCSI LUNを作成し、グループに追加する。 残念ながらLUNの最大サイズは全容量の90%とかなり少なく、10%もの領域が余る上に、ディスク全容量の21.25%も削られてしまうことになり、なかなかつらい。フルディスク使える場合はRAID6を使うのとあまり変わらないと考えると余計に辛い。

ReadyNASには暗号化機能があり、起動時に暗号化キーを含むUSBディスクを挿すことで起動できる仕組み。 どうもこれはLUKSによるものであるらしい。

また、ReadyNASは4つものNICを搭載している。 チーミングも可能だが、「グローバルLANへの経路を持つネットワーク」と「ストレージ専用ネットワーク」の2つに同時に接続できるという点は大きい。

Btrfsボリュームの構成

従来はなんらかのディスク(基本的には物理1台に対して1デバイス)に対してdm-cryptデバイスを作成し、dm-cryptデバイスをBtrfsディスクとして使用していた。

今回はRAIDレイヤー及び暗号化レイヤーはブロックデバイスが保証しているものとして取り扱う。 つまり、「このブロックデバイスは冗長化され、暗号化されたブロックデバイスである」という前提が成り立つようにするわけである。

実際にReadyNSAのボリュームはハードウェア的にRAID化され、その上で暗号化もされている。 Linux上で行う場合はmdまたはLVM RAIDデバイスをLUKSで暗号化するのが有力だろう。

この方式をとることで、「冗長化され暗号化されたiSCSIディスク」を用意することができればいくらでもBtrfsを拡張できるようになり、 従来よりも堅牢性、可用性が向上した。

snashotをsendすると壊れる

ところが、実際に使おうとreadonly snapshotをsend/receiveを使って転送すると途中で「読み込み専用ファイルシステム」となってしまい、ファイルシステムが壊れる。

ネットワークデバイスなども疑ったのだが、ログを見る限りiSCSIディスクが応答しなくなって失敗している。 Btrfs的な理由を疑ってMLでも質問したのだが、追求したところどうも「Linuxカーネルの書き込みにネットワークが追いつかず失敗する」ようだった。

この対処方法としては/sys/block/<disk>/queue/max_sectors_kbの値を変更するというものだった。 Manjaro Linuxのデフォルトは8192だが、これを4096とするとトラブルは解消された。 Red Hat Enterprise Linuxでも同様の変更によりトラブルが起きたこともあるようだ。

このディスクについては/dev/diskを使うことができないので自動化が難しい。 やるとしたら次のようなところか。

おわりに

レイヤーを分担させることができるようになり、ぐっとわかりやすくなった。 また、パフォーマンス面でもそれなりに向上が見られた。コンピュータ自体も刷新されたため体感としては小さいが、起動するコンピュータを問わず簡単にworldストレージをマウントできるようになった点は大きい。

max_sectors_kbの問題は盲点で発見に苦労したか、無事問題が解消できてよかった。