メモだらけ RSSフィード

2018-02-16

Google Chromeを起動させると、別な仮想デスクトップで立ち上がる件

概要

  • 最近のGoogle Chrome*1で「前回開いていたページを開く」という設定をしている
  • X Window System上で仮想デスクトップ*2を使っている

という2つの条件を満たした状態でchromeを起動すると

  • 現在表示している仮想デスクトップではなく、前回chromeを終了した仮想デスクトップにchromeのウインドウが現れる

という挙動を示す。

問題点

上記挙動だと、chromeを立ち上げてコンテンツを閲覧するためには、

  1. chromeを立ち上げる
  2. 現在表示している仮想デスクトップにウインドウが現れなかった場合は、chromeのウインドウが表示されている仮想デスクトップに切り替える

という操作をする必要があり、めんどくさい。


解決策

chromeを立ち上げた際に、現在表示している仮想デスクトップにてウインドウを表示させるには、

$ google-chrome --window-workspace=$(wmctrl -d | grep '*' | cut -d ' ' -f1)

とすれば良い*3

理由

https://groups.google.com/a/chromium.org/d/topic/chromium-reviews/ASrkxkIPJ6k/discussion にて、X Window Systemの仮想デスクトップのサポートが入ったため。

以下引用。

Description:

Add support for X11 workspaces

The core changes introduced:

  • Add new observers that will get notified when a window's workspace

changes.

  • Save the browser window workspace on browser close
  • Restore the browser workspace for a restored browser session
  • Switch to the window's workspace when it is Activate()'d

引用終わり。

Chromiumのスレだが、chromeも一緒のはず……実際に、Debian 9.3 + Cinnamon 3.2.7-4上で動くGoogle Chrome 64.0.3282.167でも同じ挙動になるのを確認した。

この変更で、親切なことに、chromeの起動時にウインドウを表示する仮想デスクトップを指定するオプション、--window-workspace=${HOGEHOGE}が追加されている。

${HOGEHOGE}には、chrome起動時にウインドウを表示させたい仮想デスクトップの番号を指定すれば良い。例えば1つ目の仮想デスクトップなら0、2つめなら1、全ての仮想デスクトップに表示するなら-1といった要領である。

*1:以下chrome

*2https://ja.wikipedia.org/wiki/%E4%BB%AE%E6%83%B3%E3%83%87%E3%82%B9%E3%82%AF%E3%83%88%E3%83%83%E3%83%97

*3:Debian 9.3 + Cinnamon 3.2.7-4 + Google Chrome 64.0.3282.167 + wmctrl 1.07にて動作を確認した。

2017-07-15

fdiskにアライメントをいい感じにやってくれる変更が入っていた

久しぶりにパーティションを切る機会があったので、雑に調査したところ、fdisk 2.27.1は、4Kセクタ系ドライブ*1のパーティションのアライメントをいい感じに調整してくれるらしいことがわかった*2。更にfdisk 2.27.1のマニュアルにはGPT*3も理解できると書かれていた。この変更は2.232.17で入ったようだ。便利。

参考文献

                /*
                 * Align the begin of partitions to:
                 *
                 * a) topology
                 *  a2) alignment offset
                 *  a1) or physical sector (minimal_io_size, aka "grain")
                 *
                 * b) or default to 1MiB (2048 sectrors, Windows Vista default)
                 *
                 * c) or for very small devices use 1 phy.sector
                 */

引用終わり。

  • no title← kernel.orgのutil-linux(これにfdiskが含まれる) 2.23のリリースノート
  • no title← kernel.orgのutil-linux(これにfdiskが含まれる) 2.17のリリースノート。
  • 2.27.1の$ man fdisk。以下引用。
All partitioning is driven by device I/O limits (the topology) by default.  fdisk is able to optimize the disk layout for a 4K-sector size and  use  an  alignment
       offset  on  modern devices for MBR and GPT.  It is always a good idea to follow fdisk's defaults as the default values (e.g. first and last partition sectors) and
       partition sizes specified by the +<size>{M,G,...} notation are always aligned according to the device properties.

引用終わり。

*1:正確にはなんていうのか知らない。

*2:条件があるようだ。man参照のこと。

*3https://ja.wikipedia.org/wiki/GUID%E3%83%91%E3%83%BC%E3%83%86%E3%82%A3%E3%82%B7%E3%83%A7%E3%83%B3%E3%83%86%E3%83%BC%E3%83%96%E3%83%AB

2017-05-31

ある端末からChromecastにキャストしようとすると、同一ネットワークにあるChromecastのみがキャスト先として現れる理由

この文書について

ある端末からChromecastにキャストしようとすると、同一ネットワークにあるChromecastのみがキャスト先として現れる理由、及び、その調査をしてきて出てきた疑問点が書かれている。

ChromecastのIPアドレスを取得する

端末からキャストするためには、キャスト先のChromecastのIPアドレスがわかっていないといけない(no titleの「What is Chromecast?」節を参照のこと)。no titleの「Problem Statement」節によると、「Chromecast discovery packets」のTTLは1に設定されている。これにより、このパケットはルータを超えられない。

また、no titleの「Chromecast」節によると、ChromecastのIPアドレスを取得する仕組みには、version1とversion2の2つがあり、version2が現在ポピュラーなようだ。version2はDIALではなくmDNSを発見のために用いているが、この場合もmDNSのTTLが1に設定されているようだ。

これらの事柄から、ある端末でキャスト先候補を取得しようとすると、ルータを超えたChromecastのIPアドレスを取得できないため、そのChromecatはキャスト先に現れないと推測される。

疑問点

TTL関係ないのでは疑惑

パケットキャプチャしてみたところ、手持ちのversion2のChromecastへのmDNS問い合わせパケットのTTLは64、mDNS応答パケットのTTLは255であった。しかし、家庭にChromecastが2台ある場合に、誤爆を防止する方法 〜ネットワークプリンタ共有編〜 - メモだらけは実現できている。これは前節の調査結果と矛盾する。追加調査したところ、どうやらTTLは関係ないようだ。

以下マルチキャストルーティングの仕組み その1 (ネットワークのおべんきょしませんか? Cisco CCNA/CCNP/CCIE、ネットワークスペシャリスト試験の勉強にピッタリ)より引用。

マルチキャストとブロ ードキャストを同じように扱うのは、ルータも同じです。つまり、あるインタ フェースで受信したマルチキャストは他のインタフェースに転送しません。

引用終わり。

以下、no titleより引用

一方、ルータ(レイヤ3スイッチ)では、やはりブロードキャストと同じ扱いで、デフォルトではマルチキャストパケットをブロックする。つまり、マルチキャストパケットはネットワークを超えて転送されないことになる。

引用終わり。

マルチキャストがルータを超えられないのはデフォルトなので(機種による?)、超えるようにすることもできるようだ(IGMPとかでググれば情報が出てくると思う)。


IP総当りによる、ルータを超えた操作受付の可能性

Chromecastがつながっているであろうネットワークが取りうるすべてのIPアドレスに対して、ある動画をキャストするような指示を送れば、もしかしたらルータを超えてもキャストできるかもしれない。また、どの動画を再生しているかという情報を得ることができるかもしれない。これを防ぐためにには、ファイアウォールの適切な設定*1や、NATの利用が考えられる*2

# 2017-11-07追記 Chromecastに「キャストされているメディアを他の端末で制御」という設定項目が追加されており、これを無効にすると、あるスマホで再生を開始した動画が、同じネットワークに繋がっている他のスマホで操作できなかった。なので、IP総当りによる、ルータを超えた操作受付の可能性に関しては気にしなくて良さそう。

*1:30分ほど調査したが、設定するための情報が見つからなかった。

*2:これだと容易に遮断できるだろう。

2017-05-30

GuakeもYakuakeも便利

この文書について

Guake 0.4.4-1の不満点 - メモだらけの続きである。

本文

Guake 0.4.4-1の不満点 - メモだらけであげた2つの不満点だが、Ubuntu 16.04 LTSのGuake 0.8.4-1だと一部解決されていた。

不満点1

マウスカーソルがあるディスプレイにターミナルを表示するという設定ができない

首を振ると疲れるので、だいぶ前にデュアルディスプレイをやめている。そのため、この項目は未検証である。

不満点2

ショートカットにCtrl+Alt+Fを指定した時に、それが有効にならない

なぜか知らないけど、有効になった。快適。

結論

GuakeもYakuakeも便利なので、あなたのデスクトップ環境に応じて使い分けるといいと思う。

2017-04-16

libvirtでguestの起動順序を設定する

動機

libvirtを使っていて、DBサーバとアプリケーションサーバを分けているのだが、DBサーバをアプリケーションサーバより先に起動しておかないと、アプリケーションサーバで動くRailsアプリが起動時にエラーを吐く。これを解消したい。

結論

ここに書かれている。↓

[libvirt] libvirt-guests - order of stopping/starting guests

Debian jessieでバージョン1.2.9-9+deb8u3のlibvirt*を用いた時に、この挙動になることを確認した。

なんでそうなるの……

どうやらlibvirt + qemuの組み合わせだと、guest起動順はconfigDir(/usr/local/etc/libvirt/qemu。パッケージからインストールすると、多分/etc/libvirt/qemuになる)をreaddir(3)した順序に依存するようだ。

詳細はlibvirt + qemuにおけるguestの起動順序(1) 〜libvirt側〜 - メモだらけに書いてある。


# 2017-04-16 追記

libvirtにguestの起動順を指定する機能は入らないようだ。参考→Re: [libvirt] staggered guest startup / startup order

However, considering that the boot order is most likely dependent

on a higher level application (the app hosted in the guest) and

it's dependencies, does it really make sense to add that integration

to libvirt/libvirtd?

とのことです……('・c_・` )ソッカー

libvirt + qemuにおけるguestの起動順序(1) 〜libvirt側〜

結論

libvirt + qemuの時のゲスト起動順序は/usr/local/etc/libvirt/qemu(または/etc/libvirt/qemu)に置かれている設定ファイル達の、readdir(3)による取得順序に依存すると思われる。なお、Ubuntu Manpage:Not Foundによると、readdir(3)での取得順序はファイルシステムの実装依存になっているようだ。また、Simple Directory Lister (The GNU C Library)によると、ランダムなようだ。どっちが正しいかは不明だが、安全側に倒すならば、(libvirt内で)何らかのソートをかける必要があるだろう。

# 2018-01-13追記 readdir(3)による取得順序は特に保証されていないようだ(なので、UbuntuのやつもGNUのやつも間違った実装じゃないので、どっちが正しい、ということはない。)(参考:c - Does readdir() guarantee an order? - Stack Overflow)。

詳細

このエントリ内にあるtools/$HOGEHOGEなどのファイルは、2017/04/16時点での最新のUbuntu16.04において、apt-get source libvirt-binとして取得した(libvirt-1.3.1)。

ゲスト起動順に着目したlibvirtd-guest起動時の処理

tools/libvirt-guests.sh.inが、/etc/init.d/libvirt-guestの元ネタのようだが、これのstart関数内(L505)で$LISTFILEにかかれている順序でguestが起動されている。このファイルは同ファイル内のstop関数で作成されており、この関数内(L475)でlist_guestsが呼ばれていて、これの返り値の順番で$LISTFILEが書き込まれている。list_guestsの定義は以下の通り。

# list_guests URI PERSISTENT                                                                                        
# List running guests on URI.                                                                                       
# PERSISTENT argument options:                                                                                      
# --persistent: list only persistent guests                                                                         
# --transient: list only transient guests                                                                           
# [none]: list both persistent and transient guests                                                                 
list_guests() {
    uri=$1
    persistent=$2

    list=$(run_virsh_c "$uri" list --uuid $persistent)
    if [ $? -ne 0 ]; then
        RETVAL=1
        return 1
    fi

    echo "$list" | grep -v 00000000-0000-0000-0000-000000000000
}

となっている。ここから定義を辿って行くと、結局同ファイルL90で定義されているrun_virshにたどり着く。run_virshの定義は以下の通り。


# run_virsh URI ARGUMENTS...                                                                                        
# start virsh and let it execute ARGUMENTS on URI                                                                   
# If URI is "default" virsh is called without the "-c" argument                                                     
# (using libvirt's default connection)                                                                              
run_virsh() {
    uri=$1
    shift

    if [ "x$uri" = xdefault ]; then
        virsh "$@" </dev/null
    else
        virsh -c "$uri" "$@" </dev/null
    fi
}

なので、virsh list時の挙動を調べれば、guestの起動順がわかることになる。

listコマンドの定義はtools/virsh-domain-monitor.cのdomMonitoringCmds内にあり(L2367)、ハンドラとしてcmdListが登録されている。

tools/virsh.cのcmdGroups内にdomMonitoringCmdsが使われており(L857)、同ファイルのmain内(L943)で、vshInitがこれとともに呼ばれている。その後、tools/virsh.cのmainでvshCommandRunというそれらしき関数が呼ばれている。この関数はtools/vsh.cで定義されており、この中でコマンドを順次実行していっている。この中でハンドラを呼んでいるようなので、こいつを調べていけばいいだろう。

というわけで、tools/virsh-domain-monitor.cのcmdListを見ていくと、guestの取得順は、virshDomainListPtr->ndomainsに依存しており、これはvirshDomainListCollectという関数で設定されているようだ。virshDomainListCollectはtools/virsh-domain-monitor.cのL1497で定義されており、virConnectListAllDomainsという関数の結果に依存している。


#2017-05-13 virConnectListAllDomainsを調べるべきところをvirConnectListDomainsを調べていたので修正。ただし結論は変わらなかった。記録として元の方も残しておく。

#正しいほうここから

virConnectListAllDomainsの定義はsrc/libvirt-domain.cのL6696にあり、以下の通り。

int
virConnectListAllDomains(virConnectPtr conn,
                         virDomainPtr **domains,
                         unsigned int flags)
{
    VIR_DEBUG("conn=%p, domains=%p, flags=%x", conn, domains, flags);

    virResetLastError();

    if (domains)
        *domains = NULL;

    virCheckConnectReturn(conn, -1);

    if (conn->driver->connectListAllDomains) {
        int ret;
        ret = conn->driver->connectListAllDomains(conn, domains, flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(conn);
    return -1;
}

#正しい方ここまで


#元のものここから

virConnectListDomainsの定義はsrc/libvirt-domain.cのL55にあり、以下の通り。

/**                                                                                                                 
 * virConnectListDomains:                                                                                           
 * @conn: pointer to the hypervisor connection                                                                      
 * @ids: array to collect the list of IDs of active domains                                                         
 * @maxids: size of @ids                                                                                            
 *                                                                                                                  
 * Collect the list of active domains, and store their IDs in array @ids                                            
 *                                                                                                                  
 * For inactive domains, see virConnectListDefinedDomains().  For more                                              
 * control over the results, see virConnectListAllDomains().                                                        
 *                                                                                                                  
 * Returns the number of domains found or -1 in case of error.  Note that                                           
 * this command is inherently racy; a domain can be started between a                                               
 * call to virConnectNumOfDomains() and this call; you are only guaranteed                                          
 * that all currently active domains were listed if the return is less                                              
 * than @maxids.                                                                                                    
 */
virConnectListDomains(virConnectPtr conn, int *ids, int maxids)
{
    VIR_DEBUG("conn=%p, ids=%p, maxids=%d", conn, ids, maxids);

    virResetLastError();

    virCheckConnectReturn(conn, -1);
    virCheckNonNullArgGoto(ids, error);
    virCheckNonNegativeArgGoto(maxids, error);

    if (conn->driver->connectListDomains) {
        int ret = conn->driver->connectListDomains(conn, ids, maxids);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();
 error:
    virDispatchError(conn);
    return -1;
}

#元のものここまで。


conn->driverを見ていく必要がありそう。


libvirtdによるドライバの初期化

libvirtは各種ドライバを読み込むことでいろいろな仮想化ソフト(qemuとかVirtualBoxとか)に対応しているようだ。

具体的には、src/libvirt.cのvirStateInitializeで、virStateDriverTabに登録されている各種ドライバの初期化をしている。

virStateDriverTabへの登録はsrc/libvirt.cのvirRegisterStateDriverで行われている。daemon/libvirtd.cのdaemonInitializeでvirRegisterStateDriverが呼ばれており、daemon/libvirtd.cのmainのL1416でdaemonInitializeが呼ばれている(蛇足:コンパイル時に、どの仮想化ソフトのドライバを読むか決定しているようだ)。

qemuのドライバの初期化はsrc/qemu/qemu_driver.cのqemuStateInitializeだが、ここで、virDomainObjListLoadAllConfigsが呼ばれている(L893)。virDomainObjListLoadAllConfigsはsrc/conf/virdomainobjlist.cのL502で定義されていて、virDirReadという関数で設定をconfigDirから読みだしている。virDirReadの定義はsrc/util/virfile.cのL2737にある。これがreaddir(3)を使っている。

また、configDirの定義はsrc/qemu/qemu_conf.cのL259にあり、これによると/usr/local/etc/libvirt/qemuとなる。

Connection: close