BusyBoxというのがある。psとかls等の一連のコマンド群をまとめたものだ。組み込みシステムで使われる。容量的な問題と、コマンドを1つ1つをその環境下でコンパイルする手間を省くためらしい。てっきり、BusyBoxそのものがシェルで、それらがその内部コマンドとして処理されているものとばっかし思っていた。
BusyBoxはシェルでないようだ。それではどうしてシェルから与えたコマンドをそれぞれのコマンドとして処理できるのか? 答えはシンボリックリンクである。このようなリンクの使い方があるのかと感心させられた。
ファイル1を起動する。そのファイル1はファイル2へのシンボリックリンクだとする。プロセスとしてはファイル2が起動することになる。ただし、ファイル2が受け取るコマンドパラメータは、ファイル1が受け取るコマンドパラメータと同じになる。従ってファイル2のコマンド引数名はファイル1ということだ。
BusyBoxはps,ls.pwd等のファイル名がBusyBoxへとリンクすることで、BusyBox内のそれぞれのコマンド処理を動作させる。
プロセスはforkで親プロセスの複製を作成した後、execveで本来のプログラムと差し替える。それを行うのがdo_execveである。do_execveの引数はリンク元としてのコマンド引数である。ここではファイル1である。open_execでファイル1のfile構造体を取得する。open_execは最終的に__link_path_walkがコールされ、従ってこのfile構造体はリンク先を展開したdentry,inodeを有するfile構造体となる。
実プログラムを差し替えるのは、たぶんsearch_binary_handler。その引数としてlinux_binprm *bprmを使われる。そして bprm->fileにはリンク展開したfile構造体がセットされるが、bprm->filenameとbprm->interpには、do_execveで渡された引数、すなわちコマンドファイル1のコマンド引数がそのまま設定されている。
int do_execve(char * filename,
char __user *__user *argv,
char __user *__user *envp,
struct pt_regs * regs)
{
struct linux_binprm *bprm;
struct file *file;
struct files_struct *displaced;
int retval;
file = open_exec(filename);
retval = PTR_ERR(file);
if (IS_ERR(file))
goto out_kfree;
sched_exec();
bprm->file = file;
bprm->filename = filename;
bprm->interp = filename;
retval = bprm_mm_init(bprm);
if (retval)
goto out_file;
bprm->argc = count(argv, MAX_ARG_STRINGS);
if ((retval = bprm->argc) < 0)
goto out_mm;
bprm->envc = count(envp, MAX_ARG_STRINGS);
if ((retval = bprm->envc) < 0)
goto out_mm;
retval = security_bprm_alloc(bprm);
if (retval)
goto out;
retval = prepare_binprm(bprm);
if (retval < 0)
goto out;
retval = copy_strings_kernel(1, &bprm->filename, bprm);
if (retval < 0)
goto out;
bprm->exec = bprm->p;
retval = copy_strings(bprm->envc, envp, bprm);
if (retval < 0)
goto out;
retval = copy_strings(bprm->argc, argv, bprm);
if (retval < 0)
goto out;
current->flags &= ~PF_KTHREAD;
retval = search_binary_handler(bprm,regs);