hylom'sWeblog

- hylom's blog
| about hylom / hylom.net | old contents | login

AVFoundation小ネタ1:AVAssetを使ってmp3やm4aのタグ情報を取得する

posted on 2017/05/21 01:28:53

 macOS(10.7以降)やiOS(4以降)などで利用できるAVAssetクラスを利用することで、別途外部のライブラリを使用せずにmp3ファイルやm4a(AAC)ファイルのメタデータ(タグ情報)を取得できる。AVAssetはさまざまな動画や音声ファイルに対応しており、同様のやり方で動画ファイルのメタデータも取得できると思われる(未検証)。

 特定のファイルのメタデータを取得するには、まずAVAsset(URL: url)メソッドを使ってAVAssetオブジェクトを作成する。url引数には対象ファイルのパスをNSURL形式で指定する。

let path = <ファイルのパス>
let url = NSURL(fileURLWithPath: path)
let asset = AVAsset(URL: url)

 メタデータにはmetadataプロパティやcommonMetadataプロパティ経由でアクセスできる。このプロパティはAVMetadataItemの配列となっている。この配列に対しfor inループなどを使ってメタデータを探して処理を行えば良い。

for metadata in asset.metadata {
    // metadataオブジェクトを使ってメタデータを得る
}

AVMetadataItemの中身 

 ここまでは簡単なのだが、AVMetadataItemクラスはさまざまな形式のファイルに対応するための抽象化がされており、ドキュメント初見では意味が分かりにくいかもしれない。

 まずkeySpaceプロパティだが、ここにはそのメタデータがどのファイル形式でサポートされているものなのかを示す文字列が格納されている。ここで使われる文字列のうちいくつかはあらかじめ定数として定義されている(Key Spaces)。たとえばmp3が格納しているタグ情報であるID3で利用できるメタデータの場合、keySpaceプロパティの値はAVMetadataKeySpaceID3(文字列としては"org.id3")になる。AVMetadataItemクラスには、指定したメタデータの配列から指定したkeySpaceに対応するメタデータだけを取り出すmetadataItemsFromArrayというクラスメソッドも用意されている。

 また、keyプロパティにはメタデータを識別する情報(キー)が格納されている。この情報は必ずしも文字列であるとは限らないため、型はStringではなく(NSCopying & NSObjectProtocol)?になっている。そのため、これを文字列として参照したい場合はidentifierプロパティを使う。

 文字列形式のメタデータキーについても定数があらかじめ定義されている。たとえばID3の場合、タイトルはAVMetadataID3MetadataKeyTitle、アルバム名はAVMetadataID3MetadataKeyAlbumTitleという定数が定義されている。たとえばタイトルとアルバム名を取り出したい場合、これを使って次のようにすれば良い。

for metadata in AVMetadataItem.metadataItemsFromArray(asset.metadata, withKey: nil, keySpace: AVMetadataKeySpaceID3) {
    guard let key = metadata.identifier else { continue }

    if key == AVMetadataID3MetadataKeyTitle {
        if let v = metadata.stringValue {
            title = v
        }
    }
    if key == AVMetadataID3MetadataKeyAlbumTitle {
        if let v = metadata.stringValue {
            album = v
        }
    }
    :
    :
}

 値が格納されているvalueプロパティについても型はStringではなく(NSCopying & NSObjectProtocol)?になっているので、stringValueやnumberValue、dateValueなどのメソッド経由で取り出すのが良い。

 さて、ファイル形式によって利用できるkeyは異なる。たとえば同じタイトルでも、ID3ならAVMetadataID3MetadataKeyTitleDescription、iTunesならAVMetadataiTunesMetadataKeySongNameというキーになっている。種別ごとにキーが異なるのは面倒なので、AVMetadataItemにはcommonKeyというプロパティが用意されており、たとえばタイトルを示すメタデータであればこのプロパティにAVMetadataCommonKeyTitle(文字列としては"title")というデータが入っている。こちらもあらかじめ定数が定義されている。こちらを利用することで、ファイル形式によらず情報を取得できる。

for metadata in asset.metadata {
    if metadata.commonKey == AVMetadataCommonKeyTitle {
        if let v = metadata.stringValue {
            title = v
    }
}

iTunes形式のメタデータに関する注意点

 iTunes形式のメタデータ(keySpaceがAVMetadataKeySpaceiTunesのメタデータ)では、identifierプロパティに「itsk/%A9nam」といった文字列が格納されている。いっぽう、あらかじめ定義されているiTunes Metadata Keysでは、たとえばタイトルを示すAVMetadataiTunesMetadataKeySongNameの値は「@nam」という文字列になっており、これらが一致しない。そのため、次のようにしてidentifierの値を変換した上で比較する必要がある。

guard let key = metadata.identifier else { continue }
// key is like 'itsk/%A9nam', so split and decode
let comps = key.componentsSeparatedByString("/")
    if (comps[0] != "itsk") {
        continue
    }
    let k = comps[1].stringByReplacingOccurrencesOfString("%A9", withString: "@")
}

Mac OS X向けEmacs 25.1で日本語入力時にちらつく問題の原因

posted on 2017/04/17 00:26:43

 Emacs for Mac OS Xで配布されているEmacsのMac OS X向けビルドでは、Emacs 24.4移行は標準でインラインでの日本語変換が可能になっている。これはEmacs 25.1でも同じだが、日本語入力時にカーソルがちらつく(1文字入力したり、変換するたびにカーソルが変換位置の先頭に一瞬移動してその後変換位置の最後に移動する)という現象が発生する。この問題はEmacs 24.5では発生していなかったので、24.5から25.1の間のどこかで混入したと思われる。そこで、EmacsのGitリポジトリをたどってこの問題がどこで発生するようになったのかを検証してみた。

 結論から言うと、9e77c1b7bcfd0807be7fe67daf73c2320e864309のコミットで問題が発生するようになっている。

 また、その1つ前のec10ba2792eef613caf47fff83e869d4bc177616のコミットでは問題は確認できなかった。

 問題のコミットで変更されているのはsrc/keyboard.cだったのがちょっと意外。てっきりMac OS X向けのコードであるns*.mあたりが原因だと思っていたのだが。おおむね問題の発生理由は想像できたのだが修正コードはもう少し周囲をチェックする必要があるのでまた今度。

 ちなみにこのコミットを見つけるために行った作業の流れだが、基本的にはいわゆる2分探索である。gitにはこの作業を自動で行うgit bisectコマンドがあるのだが、コンパイルしたバイナリに問題があるかどうかを自動で判別する手段がなかったのでビルド後にバイナリを実行して目視で問題が存在しているかをチェックしなければならなかったのが大変だった。

 (追記@5/21)パッチを作成して公開した。また、このパッチを利用せずとも(少なくともバージョン25.1および25.2では)この問題は以下のようにredisplay-dont-pause変数をnilに設定することで解決できることも発見した。

(setq redisplay-dont-pause nil)

 ただし、この変数はバージョン24.5でobsoleteとなっているので今後も利用できるかは分からない。また、Emacsのバグトラッカーに問題が報告されていたのでこれにコメントを入れておいた。

macOSアプリ開発でStoryboard+Core Dataを使うときのハマり話

posted on 2017/04/09 22:55:35

 ふと思い立って久しぶりにmacOSアプリ開発を始めて見たのだが、Core Dataで管理しているデータを(CocoaBindingを使って) Storyboardを使って作った画面上に表示するところでハマったうえにググっても情報を探すのが大変だったのでメモ。

参考文献

作業と疑問点

 Xcode(7.2.1)でOS Xの「Cocoa Application」を選択してテンプレートからプロジェクトを作成。このとき「Use Storyboards」、「Create Document-Based Application」、「Use Core Data」にチェックを入れておく。続いてAppleのCore Dataプログラミングガイドの「管理オブジェクトモデルの作成」に従ってオブジェクトモデルを作成する。

 さらにこのドキュメントでは「Core Dataスタックの初期化」を行うよう記されていて、NSManagedObjectContext、NSPersistentStoreCoordinator、NSManagedObjectModelの3つが必要と記述されている。しかし、初期化コードとしてDataControllerクラスの実装例もあるのだが、このオブジェクトをどのオブジェクトが所持すべきなのか(ビューなのかドキュメントなのか)が記載されていない。

 プロジェクト作成時に「Use Core Data」にチェックを入れておくと、ドキュメントの基底クラスとしてNSPersistentDocumentが使われるのだが、ドキュメントを見る限り、自動的にCore Data関連の初期化を行って「ready-to-use」のNSManagedObjectContextのインスタンスを提供すると書いてある。この解釈だと、自前でCore Dataスタックの初期化をする必要はなさそうである。

 「Core Data の使い方(1)」ページではViewController内でNSManagedObjectContextのインスタンスを作成し、ここでCore Dataの初期化を行っているのだが、本来Core Dataの管理はDocument(MVCにおけるModel)の仕事であるべきで、ViewController内でこの作業を行うのはよろしくないのではないか、という疑問があるし、前述の通りNSPersistentDocumentを利用する場合この処理は不要であるはずである。

 しかし、Documentが持つNSManagedObjectのインスタンスをArray Controllerに参照させる手段が分からない。DocumentにArray Controllerのoutletを作成できれば、そのオブジェクトのsetManagedObjectContextを使ってNSManagedObjectContextインスタンスを渡すことはできるのだが、StoryboardではDocumentにArray Controllerのoutletを作成することもできない。

 ちなみにArray Controllerを使ってTable ViewとArray Controllerをバインドしただけだと、Array ControllerにNSManagedObjectContextが関連付けられていないため「cannot perform operation without a managed object context」というメッセージが表示される。

解決法

 StackOverflowの「How do you bind a storyboard view to a Core Data entity when using NSDocument?」にそのものずばりの質問と回答があった。結論的には、まずDocument.swiftのmakeWindowControllers()の末尾に下記を追加する。

windowController.contentViewController!.representedObject = windowController.document

 これでView ControllerのrepresentedObjectがDocumentを参照するようになる。

 続いてArray ControllerのView Controllerで、Managed Object Contextで「Bind to」にチェックを入れ、「View Controller」を指定する。Model Key Pathには「representedObject.managedObjectContext」を指定する。以上で完了。ただ、このときModel Key Pathに「!」が表示されるのがちょっと気持ち悪い。

まとめ

  • 「Use Core Data」を選択してプロジェクトのテンプレートを作った場合、独自に初期化コードを書く必要は無い
  • DocumentからView Controllerにアクセスするのは面倒臭い

 なおこの辺の作業を行ったコードはhttps://github.com/hylom/ttune/tree/76d43fe476c24f064aee51b7748529aa82a309d1です。

CentOS 7でMojoliciousを使う

posted on 2017/03/09 19:58:29

 最近PerlのWebアプリケーションフレームワークMojoliciousをずっと触っているのだが、CentOS 7の標準リポジトリやEPELではパッケージが提供されていないため、別の方法でインストールする必要がある。まあPerlモジュールなのでCPANを使うのが一般的なのだが、一般的なCentOS 7環境だと依存ライブラリが不足していてインストールに失敗する模様。たとえば次のように実行すると、テスト段階でエラーが出る。

# yum install cpan
# cpan -i Mojolicious

 ということで依存しているモジュールを調べたのだが、とりあえず以下のように2つのパッケージをインストールすれば解決するようだ。

# yum install perl-Digest-MD5 perl-IO-Compress.noarch
# cpan -i Mojolicious

Fedora 25のhttpd(Apache HTTP Server)でLet's Encryptを使う

posted on 2017/02/10 20:22:39

 Fedora 25では、httpdをインストールするとデフォルトで自己署名証明書を作成してSSLを有効にする設定になっている模様。そのため、certbotコマンドでLet's Encryptで証明書を取得しようとすると失敗するようだ。

# dnf install certbot
# certbot --apche
(...ここで設定を入力する)
Failed authorization procedure. test.hylom.net (tls-sni-01): urn:acme:error:connection :: The server could not connect to the client to verify the domain :: Failed to connect to <IPアドレス>:443 for TLS-SNI-01 challenge

 対策としては、まず/etc/httpd/conf.d/ssl.confをssl.conf.orgなどにリネーム後にhttpdを再起動してSSLをいったん無効にした後、certbotコマンドを実行すれば良い。

Windows環境でのRubyのSSLエラー対策(Windowsが管理するルート証明書を使う)

posted on 2016/12/09 19:24:38

 Windows環境でRubyを使う際、以下のようなエラーが出る場合がある。これはSSLアクセスに利用できる適切なルート証明書がないために発生する。

C:/Ruby23-x64/lib/ruby/2.3.0/net/http.rb:933:in `connect_nonblock': SSL_connect
returned=1 errno=0 state=error: certificate verify failed (OpenSSL::SSL::SSLErro
r)

 対策としてはルート証明書を用意すれば良いのだが、ネットから安易にダウンロードしたくないという場合、「certmgr」というツールでWindowsが管理するルート証明書をエクスポートし、opensslコマンドでRubyが扱える形に変換して利用すれば良い。

 まずスタートメニューの「プログラムとファイルの検索」に「certmgr.msc」と入力してEnterキーを押す。certmgrが起動するので、左ペイン内の「信頼されたルート証明機関」-「証明書」を選択する。

 すると証明書一覧が表示されるので、右ペインをクリックし、Ctrl-A等を入力してすべての証明書を選択する。続いて右クリックしてコンテキストメニューの「すべてのタスク」-「エクスポート」メニューを選択する。「証明書のエクスポート ウィザード」が開くので、指示に従って進める。「エクスポートファイルの形式」では「PKCS #12」を選択し、「すべての拡張プロパティをエクスポートする」にチェックを入れておく。エクスポートされたファイルは.pfxという拡張子が付く。

 続いてこのファイルを引数に指定して次のようにopensslコマンドを実行する。Windowsにはデフォルトではopensslコマンドが含まれていないので、別途インストールするか、適当なLinuxマシンにエクスポートした.pfxファイルをコピーして実行しよう。

$ openssl pkcs12 -in <エクスポートしたファイル(.pfx)> -out <適当なファイル名.pem> -nodes

 これでRubyで扱える.pemファイルが出力される(参考:@ITの「*.pfxファイルからPEM形式で証明書や秘密鍵を取り出す」)。

 このファイルを適当な場所に保存し、SSL_CERT_FILE環境変数にそのパスを指定する。たとえばC:\Users\hylom\windows.pemとして保存した場合、以下のようになる。

$ set SSL_CERT_FILE=C:\Users\hylom\windows.pem

英語によるコミットメッセージ文例その3

posted on 2016/09/26 23:45:21

 英語でコミットメッセージを書くときにうまい表現が思い浮かばずに困ることがあるので、色々なリポジトリを見て使えそうな表現をメモしていこうという記事です。

 今回はlibreoffice/coreなどから。LibreOfficeはコミットメッセージのスタイルがバラバラ。行末ピリオドのあり/なしも統一されていない。

fails 〜 becase A

 Aのために〜が失敗する(sal: Mac OS X 10.8 fails test_log1p because its libc log1p() does not return -0.0 unmodified but as +0.0)。

That syntax is fine

 その文法は正しい(That syntax is fine with MSVC 2015 now)。

Remove code that's no longer used

 使われていないコードを削除(Remove code that's no longer used.)。

we shouldn't need explicit 〜

 明示的な〜は不要(we shouldn't need explicit lt_rtl_OUString, etc things anymore)。

Make A not B

 BではなくAを行うようにする(Make createDesktop return XDesktop, not Object)。

why this odd unused assignment thing

 この変な使われていない割り当て処理は何だ(why this odd unused assignment thing

It's called an A

 それはAと呼ばれる(It’s called an asterisk)。「star character」という記述を「asterisk」という記述に置き換えたコミット。

Wrong name on 〜

 〜に間違った名称がある(Wrong name on menu item)。

英語によるコミットメッセージ文例その2

posted on 2016/09/22 01:04:10

 英語でコミットメッセージを書くときにうまい表現が思い浮かばずに困ることがあるので、色々なリポジトリを見て使えそうな表現をメモしていこうという記事です。

 今回はtorvalds/linuxより。コミットメッセージは動詞で始めて、また最初を大文字にはせず、文末のピリオドは無しというスタイルです(そこそこ例外あり)。

Check for 〜

 〜をチェックする(mm: usercopy: Check for module addresses

Duplicate 〜

 〜を複製する(cgroup: duplicate cgroup reference when cloning sockets

Don't clear 〜

 〜を消去しない(mem-hotplug: don't clear the only node in new_node_page()

Only use〜

 〜だけを使う(drm: Only use compat ioctl for addfb2 on X86/IA64

A be not supported

 Aはサポートされない(IB/mlx4: Diagnostic HW counters are not supported in slave mode)。珍しく名詞から始まっているコミット。

Don't allow 〜

 〜を許可しない(IB/ipoib: Don't allow MC joins during light MC flush

Do validate 〜

 〜を検証する(perf/x86/intel/pt: Do validate the size of a kernel address filter

Ignore 〜

 〜を無視する(drm/i915: Ignore OpRegion panel type except on select machines

Require 〜

 〜を必要とする(fscrypto: require write access to mount to set encryption policy

Do not register 〜

 〜を登録しない(dwc_eth_qos: do not register semi-initialized device

Remove 〜

 〜を取り除く(nfp: remove linux/version.h includes

Drop support for 〜

 〜のサポートをやめる(nfp: drop support for old firmware ABIs

英語によるコミットメッセージ文例その1

posted on 2016/09/21 01:20:38

 英語でコミットメッセージを書くときにうまい表現が思い浮かばずに困ることがあるので、色々なリポジトリを見て使えそうな表現をメモしていこうという記事です。

 今回はWordPress/WordPressより。コミットメッセージは動詞で始め、最後にピリオドを付けるというスタイルです。

Correct 〜

 〜を修正する(Docs: Correct the description of `{$taxonomy}_term_new_form_tag` hook, making it more consistent with other `*_form_tag` hooks.)。

Make A 〜

 Aを〜にする(Media: Make media library searchable by filename.)。続く説明文で使われているapply A to B(AをBに適用する)も使えそう。

Let A be 〜

 Aを〜にする(Customize: Let `static_front_page` section be contextually active based on whether there are any published pages.)。

Avoid 〜

 〜を回避する(XML-RPC: Avoid a PHP notice in `::pingback_ping()` if page title was not found.)。

Ensure 〜

 〜を確実にする(Customize: Ensure nav menu items lacking a label use the title from the original object.)。

Improve 〜

 〜を改良する(Media: Improved media titles when created from filename.)。

Enable A to 〜

 〜するようAを有効にする(REST API: Enable sanitize_callback to return WP_Error.)。

Adjust 〜

 〜を調整する(Media: Adjust `test_video_shortcode_body()` after [38597].)。

Implement 〜

 〜を実装する(Customize: Implement previewing of form submissions which use the GET method.)。

Eliminate 〜

 〜を取り除く(Query: Eliminate unnecessary `wp_list_filter()` call in `get_queried_)。

Prevent A from 〜

 Aを〜から防ぐ(Menus: Prevent non-published posts/pages from being returned in search results for adding as nav menu items)。

Sanitize 〜

 〜をサニタイズする。サニタイズは不要な情報を取り除くこと(Media: Sanitize upload filename.)。

Split 〜

 〜を分割する(Role/Capability: Split meta and primitive capabilities in the helper functions in the roles and capability tests so primitive capability tests can be made more accurate.

Deprecate 〜

 〜を廃止する(Multisite: Deprecate `wp_get_network()`.)。

Don't do 〜

 〜しないようにする(Comments: Don't do direct SQL query when fetching decendants.)。

CentOS 7で「firewalld --permanent --change-interface」コマンドが動作しない問題の解決法

posted on 2016/07/12 19:57:27

 CentOS 7のfirewalldで特定のネットワークインターフェイスを特定のゾーンに関連付けるには、「firewall-cmd --zone=<ゾーン> --change-interface=<インターフェイス>」コマンドを使えとmanページに書いてある。このコマンド自体は正常に働くのだが、設定を永続的に保存するための「--permanent」オプションがうまく動かない。具体的には、firewalldを再起動すると設定が消えてしまう。

 実は、CentOSの元になっているRed Hatの「Bug Fix Advisory firewalld bug fix and enhancement update(2015年3月5日付け)を見ると、「The "--permanent --add-interface" option is supposed to be used only for interfaces that are not managed by the NetworkManager utility」と書いてある。つまり、nmtuiなどのツール経由で設定された、NetworkManager下で管理されているネットワークインターフェイスについては、--add-interfaceや--change-interfaceオプション利用時に--permanentオプションを指定しても効果が無いようだ。

 対処策としては、NetworkManager側でネットワークインターフェイスとゾーンの対応付けを行えば良い。ただし、nmtuiコマンドではゾーンの設定項目が無いため、nmcliコマンドでの設定が必要となる。

 まず、「nmcli c」コマンドで認識されているネットワークコネクションを確認する。

# nmcli c
名前                UUID                                  タイプ          デバイス
Wired connection 1  d2ee88e7-2e1c-4168-a1ad-8bd5b6ac4db3  802-3-ethernet  eth1
System eth0         5fb06bd0-0bb0-7ffb-45f1-d6edd65f3e03  802-3-ethernet  eth0

 ネットワークコネクションの詳細設定は「nmcli c show <コネクション名>」で確認できる。

# nmcli c show "Wired connection 1"
connection.id:                          Wired connection 1
connection.uuid:                        d2ee88e7-2e1c-4168-a1ad-8bd5b6ac4db3
connection.interface-name:              --
connection.type:                        802-3-ethernet
connection.autoconnect:                 yes
connection.autoconnect-priority:        0
connection.timestamp:                   1468320004
connection.read-only:                   no
connection.permissions:
connection.zone:                        --
connection.master:                      --
connection.slave-type:                  --
connection.autoconnect-slaves:          -1 (default)
connection.secondaries:
connection.gateway-ping-timeout:        0
connection.metered:                     不明
802-3-ethernet.port:                    --
802-3-ethernet.speed:                   0
802-3-ethernet.duplex:                  --
802-3-ethernet.auto-negotiate:          yes
802-3-ethernet.mac-address:             9C:A3:BA:28:8B:E2
802-3-ethernet.cloned-mac-address:      --
802-3-ethernet.mac-address-blacklist:
802-3-ethernet.mtu:                     自動
802-3-ethernet.s390-subchannels:
802-3-ethernet.s390-nettype:            --
802-3-ethernet.s390-options:
802-3-ethernet.wake-on-lan:             1 (default)
802-3-ethernet.wake-on-lan-password:    --
ipv4.method:                            manual
ipv4.dns:
ipv4.dns-search:
ipv4.addresses:                         192.168.1.10/24
ipv4.gateway:                           --
ipv4.routes:
ipv4.route-metric:                      -1
ipv4.ignore-auto-routes:                no
ipv4.ignore-auto-dns:                   no
ipv4.dhcp-client-id:                    --
ipv4.dhcp-send-hostname:                yes
ipv4.dhcp-hostname:                     --
ipv4.never-default:                     yes
ipv4.may-fail:                          yes
ipv6.method:                            auto
ipv6.dns:
ipv6.dns-search:
ipv6.addresses:
ipv6.gateway:                           --
ipv6.routes:
ipv6.route-metric:                      -1
ipv6.ignore-auto-routes:                no
ipv6.ignore-auto-dns:                   no
ipv6.never-default:                     no
ipv6.may-fail:                          yes
ipv6.ip6-privacy:                       -1 (不明)
ipv6.dhcp-send-hostname:                yes
ipv6.dhcp-hostname:                     --
GENERAL.名前:                           Wired connection 1
GENERAL.UUID:                           d2ee88e7-2e1c-4168-a1ad-8bd5b6ac4db3
GENERAL.デバイス:                       eth1
GENERAL.状態:                           アクティベート済み
GENERAL.デフォルト:                     いいえ
GENERAL.デフォルト6:                    いいえ
GENERAL.VPN:                            いいえ
GENERAL.ゾーン:                         --
GENERAL.DBUS パス:                      /org/freedesktop/NetworkManager/ActiveConnection/679
GENERAL.CON パス:                       /org/freedesktop/NetworkManager/Settings/1
GENERAL.スペックオブジェクト:           /
GENERAL.マスターパス:                   --
IP4.アドレス[1]:                        192.168.1.10/24
IP4.ゲートウェイ:
IP6.アドレス[1]:                        fe80::9ea3:baff:fe28:8be2/64
IP6.ゲートウェイ:

 ここで、「connection.zone」というのがゾーン設定になる。デフォルトではゾーンが指定されていないが、これを変更するには「nmcli c mod <コネクション名> <設定対象> <設定する値>」コマンドを実行する。たとえば、eth1を「internal」ゾーンに指定する場合は次のようになる

# nmcli c mod "Wired connection 1" connection.zone internal

 これでfirewalldを再起動すると、eth1がinternalゾーンに割り当てられるようになる。

# systemctl restart firewalld
# firewall-cmd --get-active-zones
internal
  interfaces: eth1
public
  interfaces: eth0