OSvのCLI周辺コードをみてみる
※この記事は OSv Advent Calendar 2014 9日目の記事です
最近、ちょこちょことOSvを触らせてもらってるので、OS関連周りにはあまり明るくないながらも書かせてもらう事にしました。 こっち方面を専門としている訳ではない為、間違っている箇所などありましたらご指摘ください。
さて、CLI周りについてなのですが、前回のOSvもくもく会#4の時にこの辺りの構造について見ていたのでそこら辺触れてみようかと。adventcalendar始まってみたら @syuu1228 さんとかぶり気味だったので変えようかと思ったけど、そんなにホイホイとネタが出てくるわけでもないので突き進みます。
OSvのCLIについて
元々、OSvがjavaアプリを動かすという想定で始まっている事もあり、しばらくはCLIのシェルとしてCRaSHってのを使ってました。それをリプレースする物として、Luaで書かれたCLIが作られたそうです。後者については既に三日目のcalendarで軽く触れられてますね。
このCLI周りについて、チラッと見てみた事を徒然と書いていきます。 そうそう、OSvは凄い勢いで機能追加やらされてるので、今回書いている事もすぐに変わってしまうかもしれません。この記事は v0.16-12-gbc3b9f6 を元に書いています。
CLIのコードを見てみる
最初の一歩
場所については先ほど触れた三日目の記事で紹介されている通り、こちらにあります。pathを見ての通り、このCLIもmoduleの1つとして実装されていますね。ディレクトリ内はこんな感じ
$ ls -1
cli.c
cli.lua
cli.so
commands
doc
lib
Makefile
module.py
rpmbuild
test
module.pyはこのmoduleの起動処理辺りが書いてある様で、内容は下記の通り
from osv.modules.api import * from osv.modules.filemap import FileMap from osv.modules import api require('lua') require('ncurses') require('libedit') require_running('httpserver') usr_files = FileMap() usr_files.add('${OSV_BASE}/modules/cli').to('/cli') \ .include('cli.so') \ .include('cli.lua') \ .include('lib/**') \ .include('commands/**') full = api.run('/cli/cli.so') default = full
cli.soをrunしてますね。この段階で、cli.cがcliコマンド(これも例の三日目の記事で使ってましたね)の実装となり、これがcli.luaに処理を渡してcommandディレクトリ配下に記述のあるluaで書かれたコマンドを実行してるのかなーって予想が立ちますね。
cli.c
そんでは、cli.cを見てみましょうか。全部トレースしていくとこの記事だけでは終わらなそうなので、要所だけ。 main()内では大筋として、テストコマンド、単一コマンド実行、シェルの起動に分かれている様です。
int main (int argc, char* argv[]) { ... if (test_command != NULL) { ... lua_getglobal(L, "cli_command_test"); ... else if (optind < argc) { /* If we have more arguments, the user is running a single command */ ... lua_getglobal(L, "cli_command_single"); ... } else { /* Start a shell */ ... lua_getglobal(L, "cli_command"); ... } ... }
こんな感じで各々のパターンに応じてluaでglobalとして作られているメソッドを読んでいる様です。
cli.lua
さて、簡単な所から見てみる事にして、cli_command_singleから見てみましょう。
function cli_command_single(args, optind) local t = {} for i = optind, #args do table.insert(t, args[i]) end cli_command(t) end
用はアレですね。例えば
$ cli -a http://192.168.122.72:8000 ls -lh
とかって記述した場合、ls -lh
の部分だけ抜き取ってcli_commandに投げる感じですかね。
cli_command_testは紙面の都合で飛ばさせてもらい、cli_commandに進んでみます。要所をピックアップすると
function cli_command(args) ... if #arguments > 0 then command = arguments[1] ... filename = command_filename(command) if file_exists(filename) then local cmd = dofile(filename) local cmd_run = true ... if cmd_run then local status, err = pcall(function() cmd.main(arguments) end) if not status then print_lua_error(command, err) end end else print_cmd_err(command, "command not found") end end end
ざっと読むと、ここに渡された引数の最初の文字列と一致するファイルを探し、そのファイルに実装されているものがコマンドの実態として扱われ、実行されるということですね。ちなみにcommand_filename()はlib/util.lua内にあり、
function command_filename(name) return string.format('%s/%s.lua', context.commands_path, name) end
となっています。commands_pathは巡り巡ってcli.c内にて #define CLI_COMMANDS_PATH "/cli/commands"
として定義されていますね。以上より、CLI実行時、各コマンドの処理は/cli/commands内の当該luaファイルが実行されている事まで追えました。ls
コマンドなら/cli/commands/ls.lua
ですね。
コマンドの実装
ということで、コマンドを実装してやるには先述のディレクトリへluaプログラムを置いてやればいいんだろうという判断に至ったわけですが、これ、散々述べている様にLuaで書かれてるんですよ・・・。なんか足りないコマンドを実装してやろうとは思ったんですが、Luaはneovimで使うぞー!って聞いて本買ってみた程度なので、実装能力はほぼ絶無な為、今のところ断念中。
これはOSv全体にわたって言える事なんですが、使ってる言語多すぎでは・・・。ぱっと見た限りでもC++11、python、go、luaが飛び交っております。でもLuaはちょっと興味あるので、これを機に弄ってみるとおもしろいんじゃないかとは目論んでます。
というわけで
何はともあれ、現在private betaが行われているOSv。まだまだとりつく島もないような大規模ではないので、興味あったら読んでみる(できれば何か実装してみたりする)と面白いんじゃないかと思います。パッチの送り方等については四日目の記事を参照ください。