d.sunnyone.org
sunnyone.org

ページ

2016-05-10

Xubuntu 16.04 をインストールして最初に行う設定

Xubuntuは、XFceを使ったUbuntuのフレーバー。ここからダウンロードできる。デフォルトではGNOME2ライクな見た目の画面が使える。


そのままでも使えるのだが、自分はもうちょっとWindowsライクに使いたいので、いつもやっている設定を書いておく。

パネルを下側に移動する(いわゆる「タスクバー」を下に)

デフォルトでは上側にある黒いバー「パネル」には、デフォルトではアプリケーションを呼び出すメニュー、ウィンドウ一覧、インジケータ、時計が置いてある。要はタスクバーのようなものだ。これを下側に移動する。


下側に移動するには、パネルを右クリックし、パネル>パネルの設定を選択し、「パネル」ウィンドウを開く。

そして「パネルをロックする」チェックボックスをオフにして、パネル左側に出る取っ手を掴んで下に移動する。

Whisker Menu を押しやすくする(「スタート」ボタンを作る)

左下にある(左下に移動された)青いボタンを押すといわゆるスタートメニュー的なもの(Whisker Menuという名前らしい)が出てくるが、青いアイコンだけなので押しにくい。領域を増やして押しやすくする。

領域を増やすには、まずWhisker Menuを右クリックして「プロパティ」を選択し、「Whisker Menu」ウィンドウを開く。


「パネルボタン」の「表示」を「アイコンとタイトル」に変更して、「タイトル」を表示するようにする。「タイトル」はデフォルトでは「Application Menu」であるが、これはちょっと大きすぎるので自分は「Start」にしている。

時計の表示内容を変更

右下の時計はデフォルトでは「4 5月, 15:03」みたいな変な感じなので、カスタマイズしてそれっぽくする。

時計の表示内容をカスタマイズするには、時計を右クリックして「プロパティ」を選択して「時計」ウィンドウを表示する。


あとは「表示形式」を「カスタム」に設定し「%m/%d(%a) %H:%M:%S」などとする。選べる値はヘルプボタンを押せば飛べるがhttp://docs.xfce.org/xfce/xfce4-panel/clockを見ればわかる。

クイック起動(ピン)のショートカットを追加

クイック起動やピン留めのように、アプリケーションをパネルから起動したい。

パネルにアプリケーションの起動ボタンを配置するには、Whisker Menuを起動し、アプリケーションを右クリックして「パネルに追加」を選ぶ。

すると、パネル右側にアイコンが現れるので、右クリックして移動を選び、好きな場所に配置する。

これでだいたい定番の設定はおしまい。パネルを右クリックして「新しいアイテムの追加」を選ぶと、他にも増やせるものがあるので、好きに選ぶといいかも(ワークスペーススイッチャーとか)

おまけ: $HOMEのディレクトリを英語にする

デフォルトでは$HOMEに置いてある「デスクトップ」や「ピクチャ」はカタカナでcdしにくい。Xubuntuに限った話ではないが、これでは不便なのでいつもこれをしている。

$ LANG=C xdg-user-dirs-gtk-update

変えていいか確認するウィンドウが現れるので、従えばOK。

おまけ2: キーボードショートカットの変更(2017/6/1追記)

画面では設定>キーボード>アプリケーションショートカットキーから設定できる。

毎回やるのはだるいのでコマンドで覚えておく

# 一覧
xfconf-query -c xfce4-keyboard-shortcuts -lv

# Ctrl+Fn でのワークスペース切り替えを無効化
xfconf-query -c xfce4-keyboard-shortcuts -l | grep '/xfwm4/custom/F' | xargs -l1 xfconf-query -c xfce4-keyboard-shortcuts -r -p

# Ctrl+Alt+Lでのロックを無効化し、Super+Lに変更
xfconf-query -c xfce4-keyboard-shortcuts -p "/commands/custom/<primary><alt>l" -r
xfconf-query -c xfce4-keyboard-shortcuts -p "/commands/custom/<super>l" --create -t string -s xflock4

参考:ターミナルなどからXfceの設定をする

2016-04-21

waifu2xAvisynthをY8に対応させた

要望があったので、気分転換に実装して、v0.0.2をリリースした。
http://waifu2x-avisynth.sunnyone.org/

あまり更新することは考えていなかったのだけど、おまけのようなU/V planeの処理を省けば良いだけだったので。

最初のリリースが2015/5/30なので、もうほぼ1年になるのか、というのが衝撃。正直この実装はwaifu2x-converter-cppとAvisynthのつなぎをしただけで、たいしたことはしていないのだけど、それでも今でもStarされたりするあたり、知名度というものはすごいなぁと思う次第である。

2016-04-13

gtk-rs のチュートリアル的文書を書いた

Gtk+のRustラッパーであるgtk-rsの入門を書いた:RustとGtk+で開発するGUIアプリケーション

ほんとはもう少しやるといいことがあるのだけど(GResource、スレッド対応など)、まぁとりあえずここまで書いてあればGUIアプリケーション書いたことある人であれば感じは掴めるのかなと。ということでRev0.1。

(少なくともいまのところは)ニーズはほとんどないと思うけど、やる気があるうちにある程度形にしておかないと絶対書かないので。自分がこれから書いていくアプリケーションを読み進めるにあたっての基礎知識となれば。

2016-04-10

メタプログラミングについて話してきました

ゆるスタック勉強会 #20 で「メタプログラミングとは」という内容で話してきました。「メタプログラミングって何?イメージがつかない」という話を受けて、イメージをつくようにするのがこの話の目的です。



本当はそれぞれの言語のやりかたについて手順を書けばよかったのですが、力つきたので、実際のライブラリから例を持ってくる方向性にしました。話している間はコードや別の資料も見ながら話していたので、このスライド自体というよりは、各スライドでポイントしているライブラリの実装あるいは類似するライブラリの実装を読んでもらうのがイメージをつかみやすいんじゃないかと思います。

それぞれの言語でニワカなところがあるので、全方位からツッコミを受けてもおかしくない内容だと思っています。何かあれば補足していきたいのでぜひどうぞ。

2016-04-01

Rust と C言語 をコールバックで行き来する(Cブリッジが必要なVer)

Rustは C FFI が強いので、はっきりしたstructとCの関数を呼び出すなんていうケースでは、それっぽいstructとexternでの関数の定義を書けば呼び出せる。コールバックもできる。
他言語関数インターフェイス

しかし、以下のようなケースではちょっとやりにくい。
  • 必要な関数の呼び出しにCマクロが必要
  • opaqueなstructの下のほうのメンバにアクセスが必要
要はC言語を解釈してもらえると楽だよねという話なので、「Cでラッパー書いてautotoolsだMakefileだなんだかんだ、ようやくつなげる」となるかと思いきや、このようなCラッパーが必要な状況についてもRustには補助がある。今回はそのやりかたについて紹介する。

自分が必要だったところがC世界からのコールバック呼び出しだったので、それについても触れる。基本的には、RustのアプリケーションがCライブラリを使うという想定。コールバックなので、行ったり来たりなのだけどね。

やりかたの概要

やりかたは概ね以下の通り。
  1. ラッパー関数をCで用意する
  2. CargoのビルドスクリプトでCソースのコンパイル方法を指示する
  3. ラッパー関数をexternで定義する
  4. ラッパー関数を呼び出すstruct & traitを定義する
  5. 実際に呼び出す
実際のソースはhttps://github.com/sunnyone/rust-bridgesampleに置いてあるので、ソースがあればいいという方はこちらでどうぞ。

やりかた

ラッパー関数の準備

Rustから呼び出したい関数を、普通の関数呼び出しにできる形で定義する。本来は何かのコードのラッパーの想定なのだが、そっち側の知識が必要となると話がややこしくなるので、ここでは単なるCソースを例として使う。



上記がsrc/bridge.cに置かれているとする。

Cargo.toml + build.rsにCソースのコンパイル方法の指示する

ここがこの記事のキモ。cargoに、置いたCソースのコンパイル方法を指示する必要がある。

まず、Cargo.tomlにビルドスクリプトが「build.rs」にあるよ、ということを指示する。またコンパイラの実行の面倒を見てくれる「gcc」crateをbuild dependenciesに入れる(gccと言っているが、MSVCも対応しているらしい。試してないが)。

[package]
(略)
build = "build.rs"

[build-dependencies]
gcc = "0.3"

[dependencies]
libc = "0.2"

dependenciesのlibcは、ビルドスクリプト的には必要ないが、C FFIではほぼ使うのでここで入れておく。

上記で定義した「build.rs」に、コンパイラの呼び出しを定義する。

extern crate gcc;

fn main() {
    gcc::compile_library("libbridgesample.a", &["src/bridge.c"]);
}

このあたりの記述、詳しくはCargoのドキュメント「Build Script Support」を参照。
本来ライブラリのリンクの指示も必要だが、ここでは一旦省く。詳しくは上記ドキュメントを参照(下でもすこし触れる)。

ラッパー関数をexternで定義する(いわゆる-sys部分)

ここからはもうRust onlyでのFFIと一緒。externでfnとstructを定義する。opaqueなstructをenumを定義するのがイディオムだそうだ。

Cargoの流儀的には、FFIのためのRustらしくない定義はなんちゃら-sysというcrateに定義するほうがそれらしい。この部分だけの差し替えがしやすいから、とのこと。

ラッパー用のstructを用意する

ffi層を叩く、Rust 的構造にあわせた呼び出し部を用意する。今回は、コールバックをクロージャでもらい、それを実際のCコールバックから呼ぶ形にした。

「外側」と「内側」の2つのstructを作る

Rust世界のコード用のデータを入れておく「内側」と、その「内側」とC関数からもらったポインタを格納しておく「外側」を用意する。


必ずしもこうでなくてよいが、こうしておくことで「内側」をBox(ヒープ)で持つことが出来、ポインタに変換してC関数のポインタに渡すことができる。

git2-rsのsrc/transport.rsでやっていたので参考にした。

Drop traitを実装しポインタが自動で捨てられるようにする

後始末大事。


コールバックとしてC側に渡す関数をexternで定義する

今回はライフタイムをRustが持っているので、キャストするだけ。ここでは、Rust側がライフタイムを握っているのでポインタを渡しているが、もしC側がライフタイムを握るようであれば、Box::into_rawBox::from_rawをうまく使うといいと思う。ライフタイムの制御から外せる。mem::forget というのもあるかな。


ラッパーを実装

あとはffi関数を呼び出すようMyStructにimplする。


呼び出しコードを書く

実際呼ぶコードはこんなかんじになる。 結果はこんな感じ。
mystruct_new
Hello, World: 11
Hello Result: 12345
mystruct_free
これでだいたい使えるはず。

おまけ:pkg-configでライブラリを参照させる

本来ラップする対象があるはずなので、リンク対象やincludeパスを指定する必要がある。基本的には、println!()で、cargoが必要な情報を渡してあげればよいが、build-dependenciesでpkg-config crateを使うと、pkg-configの結果を使える。例えばこんな感じ。 with-glib branchで使ってみてある。実際のところは、panicはやっつけなのでちゃんと分岐させたり、pkg-configに関わらず環境変数で設定できるようにしたりと、工夫がされていることが多いので、詳しくはそこらのライブラリのコードを参照。例えば、glib-sys crateはこんな感じ

2016-03-15

Rust の開発環境を作る (Ubuntu + Visual Studio Code)

今回はRustとその開発環境のセットアップの仕方について。Ubuntu 14.04環境に、Visual Studio Codeをインストールして、コード補完ができるようになるまで。Ubuntu Makeのところ以外は、Macでも大体同じじゃないかなと思う。


Rust コンパイラまわり

multirustのインストール

multirustは、stableやnightlyなどrust環境を切り替えられるようにするツール。普通にrustcをインストールしてもよいが、便利なのでmultirustで入れておく。

$ curl -sf https://raw.githubusercontent.com/brson/multirust/master/blastoff.sh | sh
(略)
$ rustc --version
rustc 1.7.0 (a5d1e7a59 2016-02-29)

rustc的にはこれでおしまい。

Visual Studio Codeまわり

アーカイブを展開するだけでも良いのだけど、便利なUbuntu Makeツールを使う。

Ubuntu Makeのインストール

$ sudo add-apt-repository ppa:ubuntu-desktop/ubuntu-make
$ sudo apt-get update
$ sudo apt-get install ubuntu-make
新しいUbuntuではubuntu-makeのppaはいらないかもしれない。

Visual Studio Codeのインストール

$ umake ide visual-studio-code
(インストールパスを聞かれるけどデフォルトでこんな感じに。)
Choose installation path: /home/yoichi/.local/share/umake/ide/visual-studio-code 

便利なので本来のコマンド名codeでシンボリックリンクを貼っておく。
$ ln -s ~/.local/share/umake/ide/visual-studio-code/code ~/.local/share/umake/bin/code

visual-studio-codeのリンクが壊れているようだけど、Gitでは直っているようなので、少しするとvisual-studio-codeでも起動できるようになると思う。

RustyCode extensionのインストール

VSCodeのRust言語用拡張のRustyCodeをインストールする。codeで起動後、Ctrl+Shift+pでコマンドパレットを起動し、install RustyCodeでインストール。

開発支援ツールまわり(racer, rustfmt)

Codeを起動してRustyCodeで書こうとすると、以下のようにエラーが出るので、必要なツールを入れる。
The "racer" command is not available. Make sure it is installed.

racer (コード補完ツール) のインストール

$ cargo install racer
(略)
  Installing /home/yoichi/.multirust/toolchains/stable/cargo/bin/racer
be sure to add `/home/yoichi/.multirust/toolchains/stable/cargo/bin` to your 
PATH to be able to run the installed binaries
cargoに言われる通りPATHを~/.bashrcなどに設定する

export PATH=$PATH:~/.multirust/toolchains/stable/cargo/bin

その後racerを実行するとわかるが、racerには参照用にRustのソースが必要なので、ダウンロードして適当なところに置いておく。

$ racer
RUST_SRC_PATH environment variable must be set to point to the src directory of 
a rust checkout. E.g. "/home/foouser/src/rust/src"
$ mkdir ~/src
$ wget https://static.rust-lang.org/dist/rustc-1.7.0-src.tar.gz
$ tar zxvf rustc-1.7.0-src.tar.gz

racer用にソースの配置場所を環境変数に設定する(~/.bashrcなどに書く)
export RUST_SRC_PATH=~/src/rustc-1.7.0/src

racerコマンドを叩いてusageが出ればOK。

rustfmt (ソースコードフォーマッター) のインストール

なくてもエラーは出ないが、入れておくとCtrl+Shift+I でコードフォーマットできる。cargo fmtでもOK.

インストールはcargoで。
$ cargo install rustfmt

Hello Worldプロジェクトの作成

試すには、適当にプロジェクトを作成してCodeで開く。
$ cargo new hello --bin
$ code hello &

src/main.rsを適当に編集したら、Ctrl+Shift+Rか、コマンドパレット(Ctrl+Shift+p)からRun Debugで実行できる。

2016-02-08

PHPが出力するsyslogの「ool www」の謎を追う

PHPからerror_log()でsyslogを吐いたときに出力される「ool www」という謎の文字列が一体なんなのか調べた。いいから回避策が知りたいという人はこちらへ。おまけが本編という人もいるかもしれない。

発端

とあるPHPアプリケーションがエラーログをPHP標準関数error_log()で吐く。それをフィルタしたくなったのだが、error_log()にはたいしたフックポイントもなく、アプリケーション自体には手を入れたくないので、とりあえずsyslogで受けてSyslogサーバーがなんとかするのがよいかと思って「error_log」の設定を「syslog」にしてみると、なぜかこんな感じに……

Feb  7 22:02:36 phptest ool www: LOG CONTENT

……この「ool www」は一体なんなんだ? Out-of-L...うーん。ここの文字列だけなら実害はないが、なんでこんなことになっているのかわからないので調べることにした。

テストファイル

当該のPHPアプリケーションは置いといて、1ファイルでも再現するので、以下のPHPプログラムでテストすることにする。
<?php error_log("ERRORTEST"); ?>

環境はUbuntu 14.04にnginx + php5-fpm。設定値「error_log」を「syslog」にしておく。あまりバージョンに拘っていないので、リンクが適当だったりするが気になるかたは必要なバージョンのソースを見に行ってほしい。

ソースを追う

error_log()関数の流れ

くだんのerror_log()関数をPHPから呼び出すと、こんな感じで動く。

まず、error_logのPHP_FUNCTIONの記述がext/standard/basic_functions.cにある。ここが実体である_php_error_log_ex()を呼び出す。

ext/standard/basic_functions.cの_php_error_log_ex()は、switchで引数に応じたログ出力(メールなど)を行う。今回のmessageのみの呼び出しではdefault: にたどり着き、php_log_err()を呼び出す。

php_log_err()は、main/main.cにあり、ここで「error_log」変数のチェックを行い、値が「syslog」のときはphp_syslog()関数でsyslogに出力している。

if (!strcmp(PG(error_log), "syslog")) {
 php_syslog(LOG_NOTICE, "%s", log_message);
 PG(in_error_log) = 0;
 return;
}

基本的にメッセージを引き回しているだけで、ここまでに変わった文字列処理は見受けられない。

php_syslog()関数とは

であればポイントはphp_syslog()であろうと、php_syslog()の定義をphp_syslog.hに見つけるも、こうなっていた。

なんとphp_syslog()はsyslog()であった。つまり、PHP内で何か特別な処理をすることなく、そのままsyslog()関数にメッセージを投げているだけである。

syslog関数の使い方

こうなってくるとsyslog(3)が大事になる。manがあり、日本語が古くないようなので日本語のsyslog(3)を見てみる。

プロトタイプとしては、こうなっているようだ。
#include <syslog.h>

void openlog(const char *ident, int option, int facility);
void syslog(int priority, const char *format, ...);
void closelog(void); 

呼び出しとあわせると、priorityがLOG_NOTICEで、formatが"%s"、%s部分としてlog_messageが渡ることになる。

説明を読むと
ident で指定した文字列は各メッセージの前に付与される。
とあるので「ool www」の部分はidentのようだ。

処理の中でとくにopenlog()する処理は見受けられなかったので
openlog() は必須ではなく、必要に応じて syslog() から呼び出される。 syslog() が呼び出した場合、 ident のデフォルト値は NULL になる。
の部分に該当し、
通常は ident にはプログラム名が設定される。 ident が NULL の場合、プログラムが ident として使用される
とあるので「ool www」はプログラム名のようだ。しかし「ool www」なんてプログラムはいないぞ?

syslog()関数の実装

こうなると、identがNULLだったときのopenlog()の動きを知りたくなる。そこでglibcを見たところ、openlog()の実体はmisc/syslog.cにある。

openlog()は内部のopenlog_internal()を呼び、ここでidentはLogTag変数に代入される。

LogTagがNULLのときの処理を探してみると、syslog()のログ処理の実体である__vsyslog_chk()に以下の記述があった。

if (LogTag == NULL)
      LogTag = __progname;

__prognameは何かと見ると、頭のほうでexternで宣言されていた。
extern char *__progname; /* Program name, from crt0. */
だいぶあやしい。

「__progname」とは

で、誰が__prognameをセットしているのかと探してみると「misc/init-misc.c」にあった。

void
__init_misc (int argc, char **argv, char **envp)
{
  if (argv && argv[0])
    {
      char *p = strrchr (argv[0], '/');
      if (p == NULL)
         __progname = argv[0];
      else
         __progname = p + 1;
      __progname_full = argv[0];
    }
}
__prognameは「argv[0]の最後の'/'より後」のようだ。なるほどargv[0]の役目を考えると妥当そうに見える。__progname_fullとしてargv[0]も保持されている。

php-fpmのargv[0]ってなんだっけ?

phpが動作するのは、php-fpmプロセス。そういえばphp-fpmは独自設定した文字列がpsで見えてたよなと見てみると、こうなっている。

$ ps -ef | grep '[p]hp-fpm'
root      2664     1  0 22:02 ?        00:00:00 php-fpm: master process (/etc/php5/fpm/php-fpm.conf)                    
www-data  2666  2664  0 22:02 ?        00:00:00 php-fpm: pool www                                                       
www-data  2667  2664  0 22:02 ?        00:00:00 php-fpm: pool www

php-fpmは明らかにargv[0]を書き換えている。勘のいい人ならもうお分かりだろう。一応検証するために、PHPを適当に展開してfpm.cのfpm_init()の頭と末尾(argvが書き換わる前と後)に以下のようにログを仕込んでみる(ヘッダやexternは適宜挿入)と...

zlog(ZLOG_NOTICE, "argv[0]: %s", argv[0]);

zlog(ZLOG_NOTICE, "__progname: %s", __progname);
zlog(ZLOG_NOTICE, "__progname_full: %s", __progname_full);

こんな風になる。

$ sudo ./sapi/fpm/php-fpm -F -y /etc/php5/fpm/php-fpm.conf 
[08-Feb-2016 00:25:23] NOTICE: argv[0]: ./sapi/fpm/php-fpm
[08-Feb-2016 00:25:23] NOTICE: fpm is running, pid 24381
[08-Feb-2016 00:25:23] NOTICE: __progname: ster process (/etc/php5/fpm/php-fpm.conf)
[08-Feb-2016 00:25:23] NOTICE: __progname_full: php-fpm: master process (/etc/php5/fpm/php-fpm.conf)
もう少しわかりやすくすると、こう。


そう、元々あった最後の/までの文字数分、新しい文字列の開始からスキップされている。ログ出力がだるいので例ではmaster processになっているが、pool wwwでどうなるかを想像するのはたやすいと思う。

結論:syslog tagに使われる__prognameを、プログラムパスであるargv[0]からディレクトリ部分を取り除いた文字列へのポインタとしてlibcが保存したあと、php-fpmがargv[0]を書き換えたため、謎の文字列「ool www」が生まれた。

一応Bug reportは作っておいた。PHPが対処すべき問題のように感じるけど、libcが文字列をコピーしておかないのが悪いって言われるとそうすかって感じなので、正直期待できない:https://bugs.php.net/bug.php?id=71544

回避策:identに妥当な値を設定するには?

暗黙のopenlog()をさせなければいい。つまり、期待のプログラムが動く前にopenlog()すればよい。

具体的には、以下のようなファイルを例えば/etc/php5/fpm/fixsyslog.phpと置いて……

php.iniのauto_prepend_fileで参照する。
auto_prepend_file = /etc/php5/fpm/fixsyslog.php

これで常にopenlog()が呼ばれるので現象は起こらない。

感想

原因に関しては、ソースを追うことですっきりさせることができた。オープンソースのありがたみって、やっぱりこういうところにあると思う。解決策は、もやもやが残るままだけど…

おまけ:php-fpmはどのようにargv[0]を書き換えているか?

php-fpmは見ての通りargv[0]を書き換えるのだけど、argvを全部足しても期待の文字数に足りないなんてこともあるはず。そんなときの処理がfpm_env.cに書かれていた。

ざっくり言うと「environがargvとメモリ空間上に連続で置いてあるから、一応確認しつつenvironにどいてもらって、そこをargv[0]として使いましょう」である。グロい!コメントによると、nginxやpureftpdもそうしているらしい。

この記事も最後なので該当コードを貼り付けておく。