向busybox中添加本身的applet

關鍵詞:buysbox、applet等。app

 

busybox經常使用於嵌入式環境,集成中斷Linux命令和工具。這些工具簡單高效。函數

下面從以下方面瞭解:工具

  • 這些命令是一個軟連接到busybox,那麼是如何從軟鏈接到busybox再到執行相應的功能的?
  • 如何添加本身的applet命令,進而擴展busybox?
  • 以及一個applet是如何嵌入到busybox環境的。

 

1. 如何從軟連接到busybox的applet調用?

在busybox環境中,調用命令好比ls,實際上是指向/bin/ls -> busybox的。ui

那麼buysbox又是如何將這個軟連接對應的實際功能的呢?spa

int main(int argc UNUSED_PARAM, char **argv)
{
...
#if defined(SINGLE_APPLET_MAIN)
...
#else

    lbb_prepare("busybox" IF_FEATURE_INDIVIDUAL(, argv));
# if !ENABLE_BUSYBOX
    if (argv[1] && is_prefixed_with(bb_basename(argv[0]), "busybox"))
        argv++;
# endif
    applet_name = argv[0];
    if (applet_name[0] == '-')
        applet_name++;
    applet_name = bb_basename(applet_name);
    parse_config_file(); /* ...maybe, if FEATURE_SUID_CONFIG */ run_applet_and_exit(applet_name, argv);
#endif
}

static NORETURN void run_applet_and_exit(const char *name, char **argv)
{
#  if ENABLE_BUSYBOX
    if (is_prefixed_with(name, "busybox"))
        exit(busybox_main(argv));--------------------------------顯示busybox幫助信息、applet列表等。
#  endif
#  if NUM_APPLETS > 0
    /* find_applet_by_name() search is more expensive, so goes second */
    {
        int applet = find_applet_by_name(name);------------------根據applet的name找到其在applet_main[]中的序號。 if (applet >= 0)
            run_applet_no_and_exit(applet, name, argv);
    }
#  endif
...
}

static int busybox_main(char **argv)
{
    if (!argv[1]) {-----------------------------------------------只有busybox狀況下,顯示幫助信息。 /* Called without arguments */
...
    }

    if (is_prefixed_with(argv[1], "--list")) {---------------------busybox --list顯示busybox全部applet。 ...
    }
...
    if (strcmp(argv[1], "--help") == 0) {--------------------------busybox --help顯示busybox幫助信息。 /* "busybox --help [<applet>]" */
        if (!argv[2])
            goto help;
        /* convert to "<applet> --help" */
        argv[0] = argv[2];
        argv[2] = NULL;
    } else {
        /* "busybox <applet> arg1 arg2 ..." */
        argv++;
    }
    /* We support "busybox /a/path/to/applet args..." too. Allows for
     * "#!/bin/busybox"-style wrappers */
    applet_name = bb_get_last_path_component_nostrip(argv[0]);
    run_applet_and_exit(applet_name, argv);-----------------------相似於執行busybox ls,而後調用ls applet。
}

void FAST_FUNC run_applet_no_and_exit(int applet_no, const char *name, char **argv)
{
...
    if (1
#  if defined APPLET_NO_test
     && applet_no != APPLET_NO_test
#  endif
#  if defined APPLET_NO_true
     && applet_no != APPLET_NO_true
#  endif
#  if defined APPLET_NO_false
     && applet_no != APPLET_NO_false
#  endif
    ) {
        if (argc == 2 && strcmp(argv[1], "--help") == 0) {-------若是是applet對應的--help。 /* Make "foo --help" exit with 0: */
            xfunc_error_retval = 0;
            bb_show_usage();
        }
    }
    if (ENABLE_FEATURE_SUID)
        check_suid(applet_no);
    xfunc_error_retval = applet_main[applet_no](argc, argv);-----根據applet_no好找到對應的函數,好比ls對應ls_main()。 /* Note: applet_main() may also not return (die on a xfunc or such) */
    xfunc_die();
}

void FAST_FUNC bb_show_usage(void)
{
    if (ENABLE_SHOW_USAGE) {
#ifdef SINGLE_APPLET_STR
...
#else
        const char *p;
        const char *usage_string = p = unpack_usage_messages();
        int ap = find_applet_by_name(applet_name);---------------根據全局變量applet_name找到對應的序號,而後根據須要找到對應的usage字符串。 if (ap < 0) /* never happens, paranoia */
            xfunc_die();
        while (ap) {
            while (*p++) continue;
            ap--;
        }
...
    }
    xfunc_die();
}

2. 如何添加applet

若須要添加本身的applet,好比在miscutils下建立一個monitor.c文件,在include建立一個monitor.h文件。.net

//config:config MONITOR-----------------------------------------------------Config.src會讀取下面內容寫入到Config.in中,用於配置monitor功能。 //config:    bool "monitor"
//config:    default n
//config:    select PLATFORM_LINUX
//config:    help
//config:      Monitor will collect system exception, daemon corruption, critical app exit. 

//applet:IF_MONITOR(APPLET(monitor, BB_DIR_SBIN, BB_SUID_DROP))--------------此句會寫入include/applets.h中,等因而聲明瞭monitor_main()函數。 //kbuild:lib-$(CONFIG_MONITOR) += monitor.o----------------------------------經由Kbuild.src生成寫入到Kbuild中,是對是否編譯monitor.c的控制。 //usage:#define monitor_trivial_usage----------------------------------------寫入到include/usage.h中,是monitor的幫助信息。 //usage:       "[-q] [-o OFF] [-f FREQ] [-p TCONST] [-t TICK]"
//usage:#define monitor_full_usage "\n\n"
//usage:       "Monitor system or app exception.\n"
//usage:     "\n    -q    Quiet"

#include "libbb.h"
#include <syslog.h>
#include <sys/un.h>

上面的config/applet/kbuild/usage,分別生成到miscutils/Config.in、include/applets.h、miscutils/Kbuild、include/usage.h四個文件中。code

因此在配置了CONFIG_MONITOR以後,根據miscutils/Kbuild會編譯monitor.c,入口函數是monitor_main()。component

3. applet是如何嵌入到busybox的?

經過applets/applet_tables.c生成可執行文件applet_tables。blog

applets/Kbuild中執行cmd_gen_applet_tables = applets/applet_tables include/applet_tables.h include/NUM_APPLETS.h,生成applet_tables.h文件,以及applet總數的NUM_APPLETS定義。ip

#define NUM_APPLETS 260
#define KNOWN_APPNAME_OFFSETS 8

const uint16_t applet_nameofs[] ALIGN2 = {
172,
401,
612,
836,
1048,
1299,
1514,
};

const char applet_names[] ALIGN1 = ""--------------------------------------在find_applet_by_name()等函數中根據applet名稱找到applet對應序號。
"[" "\0"
"[[" "\0"
"addgroup" "\0"
"adduser" "\0"
...
"zcat" "\0"
;

#define APPLET_NO_addgroup 2
#define APPLET_NO_adduser 3
...
#define APPLET_NO_zcat 259

#ifndef SKIP_applet_main
int (*const applet_main[])(int argc, char **argv) = {---------------------根據applet序號,找到對應的入口函數。
test_main,
test_main,
addgroup_main,
adduser_main,
...
gunzip_main,
};
#endif

const uint8_t applet_suid[] ALIGN1 = {
0x00,
0x00,
...0x00,
};

const uint8_t applet_install_loc[] ALIGN1 = {
0x33,
0x44,
...0x13,
};

其餘生成文件還包括:usage.h、applets.h等。

usage.h中包含了函數的幫助信息,是由usage.c編譯的usage生成的。

#define monitor_trivial_usage \
       "[-q] [-o OFF] [-f FREQ] [-p TCONST] [-t TICK]" \

#define monitor_full_usage "\n\n" \
       "Monitor system or app exception.\n" \
     "\n    -q    Quiet" \

4. 小結

一個是添加applet的路徑,新增.c和.h文件,其中最重要的是.c文件中特殊註釋:config:、applet:、kbuild:、usage:。

而後busybox編譯系統,解析.c中的註釋,並將其添加到include/applets.h、include/usage.h、include/applet_tables.h等文件中。

兩一個是applet的執行路徑,busybox的入口函數mian()根據傳入的applet_name,而後經過find_applet_by_name()找到對應序號,而後執行applet_main[]函數。即完成對applet的調用。

 

參考文檔:《擴充BusyBox,追加Applet的方法》、《如何向busybox添加本身的命令》。

相關文章
相關標籤/搜索