みなさん、ファイルシステムは好きですか。パソコンを使っていれば、毎日のようにお世話になっているはずで、その意味ではファイルシステムってご飯のようなものですね。なので、みんなファイルシステムのこと大好きだと思います。
世の中、いろんなご飯があるように、Linuxにもその他のOSにもたくさんのファイルシステムがあります。変わった食べものがあるように、変わったファイルシステムもあれば、変な自炊するようにサクサクおもしろファイルシステムを作ることができます。
ということで、まずは今回作ったf2tfsの概略図です。f2fsではないです。
図だけでは、わけわからんでしょうから、実際使ってみましょうね。
まずはmount pointを指定してスクリプトを動かしましょう。
$ sudo ./f2tfs.sh /mnt/f2t
mountしたとこを見てみましょう。 user_id
というファイルしかないですね。つまらないファイルシステムだ? いえいえ、ここに謎の数字を書いてみましょう。
$ cd /mnt/f2t
$ ls -li
total 1
2 -rw-r--r-- 1 root root 2 Apr 14 20:15 user_id
$ echo 15926668 > user_id
するとどうでしょう。謎の数字のファイルたちがでてきましたね。中身を見てみますと、どうにもておい文字列が出てきました。やったあ。
$ ls -li
total 11
1509907199010611200 -rw-r--r-- 1 root root 91 Apr 1 23:55 1509907199010611200
1509908201327763469 -rw-r--r-- 1 root root 51 Apr 1 23:59 1509908201327763469
1509912170800328706 -rw-r--r-- 1 root root 112 Apr 2 00:14 1509912170800328706
1509912225514885122 -rw-r--r-- 1 root root 22 Apr 2 00:15 1509912225514885122
1510963930205544450 -rw-r--r-- 1 root root 59 Apr 4 21:54 1510963930205544450
1510974849564372993 -rw-r--r-- 1 root root 47 Apr 4 22:37 1510974849564372993
1510974859496501253 -rw-r--r-- 1 root root 47 Apr 4 22:37 1510974859496501253
1510975098974453763 -rw-r--r-- 1 root root 55 Apr 4 22:38 1510975098974453763
1510983572399689730 -rw-r--r-- 1 root root 95 Apr 4 23:12 1510983572399689730
1510983608713981954 -rw-r--r-- 1 root root 31 Apr 4 23:12 1510983608713981954
1510990036564529160 -rw-r--r-- 1 root root 108 Apr 4 23:37 1510990036564529160
1510991713069789186 -rw-r--r-- 1 root root 104 Apr 4 23:44 1510991713069789186
1510991766572347392 -rw-r--r-- 1 root root 154 Apr 4 23:44 1510991766572347392
1511337205486452738 -rw-r--r-- 1 root root 47 Apr 5 22:37 1511337205486452738
1511337272939286529 -rw-r--r-- 1 root root 19 Apr 5 22:37 1511337272939286529
1511337376295317506 -rw-r--r-- 1 root root 37 Apr 5 22:38 1511337376295317506
1511337725479497730 -rw-r--r-- 1 root root 82 Apr 5 22:39 1511337725479497730
1511337915154333700 -rw-r--r-- 1 root root 71 Apr 5 22:40 1511337915154333700
1511339259349073922 -rw-r--r-- 1 root root 85 Apr 5 22:45 1511339259349073922
1511339988646252548 -rw-r--r-- 1 root root 35 Apr 5 22:48 1511339988646252548
2 -rw-r--r-- 1 root root 9 Apr 14 20:17 user_id
$ cat 1510983608713981954
これめっちゃきらい!
さて、なんとこれらのファイルには書くこともできます。だって"f2tfs"ですからね。
$ echo 1 > 1511337915154333700
するとどうでしょう。ツイがふぁぼられてますね! F2T! F2T!!
ちなみに他の値は書けません。"0"書くとあんふぁぼしたりしません。ふぁぼるためだけの存在なので。
中身のはなし
さて、このようにf2tfsはツイ一トを読んだりふぁぼったりできるファイルシステムなわけですが、その中身はどうなっているのでしょう。実は動かしていたスクリプトは、ファイルシステム部分のラッパとかではなく本体です。そうです、簡単にサクッと作りたかったので、今回は簡単でおなじみのシェルスクリプトでFUSEのファイルシステムを作りました。
とはいえ、シェルスクリプトで全部自力でFUSEやったり、Twitter APIやったりするのはめんどいです。そこで、FUSEとのやりとりには以下の booze を使いました。一方、 Twitterとのやりとりはmikuterにお願いしています。
github.com
boozeはbashへのプラグインとしてできていて、builtinとして"booze"コマンドが追加されます。まず、連想配列を作ります。f2t_ops[read]=f2t_read など、システムコール名をキーとして関数名をつっこみます。
f2t_read() {
...
}
f2t_write() {
...
}
declare -A f2t_ops
for name in ${BOOZE_CALL_NAMES[@]}; do
if [ "`type -t f2t_$name`" == "function" ]; then
f2t_ops[$name]=f2t_$name
fi
done
booze -o use_ino f2t_ops "$1"
これでbooze側から、システムコールに対応する関数が呼ばれてくるので、適宜関数を実装していきます。
一方、Twitterとのやりとりにはmikutterを使役します。mikutter-modeとかいうよくわからないリポジトリのプラグインを入れると、mikutterがD-Busでサービスを開始します。
github.com
gdbusコマンドを使うとどんなメソッドがあるかわかります。
$ gdbus introspect -e -d org.mikutter.dynamic -o /org/mikutter/MyInstance
node /org/mikutter/MyInstance {
interface org.freedesktop.DBus.Properties {
...
};
interface org.mikutter.eval {
methods:
ruby(in a(sv) param,
out s result);
signals:
properties:
};
};
はい。org.mikutter.evalの中にrubyというメソッドがあります。mikutterの中でrubyがevalされるというゴリゴリやべえメソッドです。入力の型が"a(sv)"となっています。つまり、string("s")とvariant("v")のタプル("(sv)")の配列("a(sv)")です。なので入力がちょっと面倒なのですが、以下のように使います。
$ gdbus call -e -d org.mikutter.dynamic -o /org/mikutter/MyInstance -m org.mikutter.eval.ruby '[("code", <"p 42">), ("file", <"org.mikutter.eval">)]'
('42',)
これをこんな感じで、rubyコードを入れて使います。
mikcall() {
read -r -d '' rb
arg="[(\"code\", <\"${rb@E}\">), (\"file\", <\"org.mikutter.eval\">)]"
log "${arg}"
sudo -u ${DBUS_USER} gdbus call -a ${DBUS_SESSION_BUS_ADDRESS} \
-d org.mikutter.dynamic \
-o /org/mikutter/MyInstance \
-m org.mikutter.eval.ruby \
"${arg}" 2>>${LOG_FILE}
}
mikcall_tweets() {
mikcall <<EOM
tw = Plugin.collect(:worlds).to_a[1]
result = nil
done = false
task = tw.user_timeline(:user_id => ${USER_ID}).next { |res|
result = res
done = true
}
while !done
sleep 0.1
end
result.map { |msg|
\\\\"#{msg.id} #{msg.created.to_i} #{msg.modified.to_i} #{msg.message}\\\\"
}.join(\\\\"<>\\\\")
EOM
}
sleep 0.1のループとかださいことしてますが、delayer-deferredよくわからんかったんじゃ。
びみょうなはまりどころ
びみょうなはまりどころを簡単に紹介しておきます。
inode番号とツイートのIDを一致させたかった
一致させたいよね。statでinode番号を教えても、デフォルトではFUSEは無視します。inode番号を使ってもらうには"-o use_ino"をmount optionにいれねばならぬのですが、boozeコマンドはそれを受け付けてくれません。なので、以下のpatchを当ててやりましょう。
diff --git a/booze.c b/booze.c
index 1231dbe2fde6..43c6d0c253da 100644
--- a/booze.c
+++ b/booze.c
@@ -639,7 +639,7 @@ static int booze_builtin(WORD_LIST* args)
fuse_argv[0] = "booze";
fuse_argv[1] = "-s";
- while ((opt = internal_getopt(args, "df")) != -1) {
+ while ((opt = internal_getopt(args, "dfo:")) != -1) {
switch (opt) {
case 'd':
fuse_argv[argidx++] = "-d";
@@ -651,6 +651,12 @@ static int booze_builtin(WORD_LIST* args)
fuse_argv = xrealloc(fuse_argv, (argidx + 1) * sizeof(char*));
break;
+ case 'o':
+ fuse_argv = xrealloc(fuse_argv, (argidx + 2) * sizeof(char*));
+ fuse_argv[argidx++] = "-o";
+ fuse_argv[argidx++] = strdup(list_optarg);
+ break;
+
default:
xfree(fuse_argv);
builtin_usage();
writeの関数では変数が設定できなかった
readの関数では標準出力に読まれるデータを出力します。ということは、writeの関数ではその逆で標準入力に書かれたデータが来ます。
ところが、これが問題で、入力を受けるために子プロセスが起動されてその中でwriteの関数が実行されます。ということは、その中で変数を更新してもそれは他の関数には反映されないんですねえ。めんどい。
ということで、こんな感じで処理しました。
JOURNAL=journal
load_journal() {
test -e ${JOURNAL} || return 0
source ${JOURNAL}
rm -f ${JOURNAL}
}
add_journal() {
local name="$1"
value=$(eval "echo \$${name}")
echo ${name}=${value@Q} >> ${JOURNAL}
}
f2t_read() {
local path="$1"
local readlen="$2"
local offset="$3"
check_file_path "$path" || return 1
load_journal
...
}
f2t_write() {
...
USER_ID="$(</dev/stdin)"
add_journal USER_ID
...
}
pretty_inspectがうざかった
mikutter-modeのプラグインの元のコードは以下のように、"pretty_inspect"でいい感じに出力してくれてきれいです。でも、改行が入ってると"abc\n" + "def"みたいな感じになってbash側で読むのがめんどいんですよね。なので、以下のように変えました。
diff --git a/plugin/mikutter_mode/mikutter_mode.rb b/plugin/mikutter_mode/mikutter_mode.rb
index 2fa8372ce675..c4e2ea55403a 100644
--- a/plugin/mikutter_mode/mikutter_mode.rb
+++ b/plugin/mikutter_mode/mikutter_mode.rb
@@ -22,7 +22,8 @@ Plugin.create(:mikutter_mode) do
notice "ruby code execute: \n#{file || code}"
r = Server.main.instance_eval(code, file || "mikutter-mode onthefly executer")
notice "returns: \n#{r.pretty_inspect}"
- [r.pretty_inspect]
+ #[r.pretty_inspect]
+ [r.inspect]
rescue Exception => e
notice "exception:"
notice e
前の方のrubyコードをよく読んでいたらお分かりですが、各ツイは" <本文>"の形で文字列され、さらにそれらを"<>"でjoinしたものが出力されます。古式ゆかしい巨大掲示板でよくやる方式ですね。
NULL文字でのjoinも考えたんですが、mikutter-modeのプラグインが死ぬのでやめました。
dbus-sendは使えない
D-Busたたくコマンドというとdbus-sendがありますが、今回の用途には使えません。dbus-sendではvariantを表現する方法がないのです。
DBUS_SESSION_BUS_ADDRESS わたすのめんどい
FUSEを動かす都合上、スクリプトはrootで動かす必要があります。一方で、mikutterをrootで動かすのはアレすぎるので、mikutterは一般ユーザの権限で動いています。なので DBUS_USER, DBUS_SESSION_BUS_ADDRESS を環境変数で渡して、うまいことやってやる必要があります。
capabilityをうまくやったりしたらいけるのかもしれないけれど、めんどかったので。
なんかmikutter側でアカウントをtwitterのに変えとかんとだめだね
うまくふぁぼれないみたい。いま気づいたよ。まあそもそも、"tw = Plugin.collect(:worlds).to_a[1]" これも雑なので。使いたくなったらうまく変えてね。
Adventしたよカレンダーまだあいてるよ!
としぁ Adventしたね Calendar 2022 #toshi_a解凍 まだあいてるよ! 登録してね!!
docs.google.com
それではまた
コードはこちら
github.com
では最後に
$ cd /mnt/f2t
$ ls [0-9]*
1509907199010611200 1510963930205544450 1510983572399689730 1510991766572347392 1511337725479497730
1509908201327763469 1510974849564372993 1510983608713981954 1511337205486452738 1511337915154333700
1509912170800328706 1510974859496501253 1510990036564529160 1511337272939286529 1511339259349073922
1509912225514885122 1510975098974453763 1510991713069789186 1511337376295317506 1511339988646252548
$ for x in [0-9]*; do echo 1 > $x; done