openwrt系统初始化分析

openwrt固件启动后,进入uboot,加载内核,启动init进程,而init进程包含在procd进程中,启动代码如下:

int
main(int argc,char **argv)
{
    pid_t pid;

    sigaction(SIGTERM,&sa_shutdown,NULL);
    sigaction(SIGUSR1,NULL);
    sigaction(SIGUSR2,NULL);

    early();//初始化根文件系统中的需要的文件和设置,early.c
    cmdline(); //从proc/cmdline获取命令行启动参数
    watchdog_init(1); //初始化watchdog, watchdog.c 

    pid = fork();
    if (!pid) {
        char *kmod[] = { "/sbin/kmodloader","/etc/modules-boot.d/",NULL };

        if (debug < 3) {
            int fd = open("/dev/null",O_RDWR);

            if (fd > -1) {
                dup2(fd,STDIN_FILENO);
                dup2(fd,STDOUT_FILENO);
                dup2(fd,STDERR_FILENO);
                if (fd > STDERR_FILENO)
                    close(fd);
            }
        }
        execvp(kmod[0],kmod);
        ERROR("Failed to start kmodloader\n");
        exit(-1);
    }
    if (pid <= 0)
        ERROR("Failed to start kmodloader instance\n");
    else
        waitpid(pid,NULL,0);
    uloop_init();
    preinit(); //执行初始脚本
    uloop_run();

    return 0;
}

preinit()函数中启动了/etc/preinit启动脚本:

void
preinit(void)
{
    char *init[] = { "/bin/sh","/etc/preinit",NULL };
    char *plug[] = { "/sbin/procd","-h","/etc/hotplug-preinit.json",NULL };

    LOG("- preinit -\n");

    plugd_proc.cb = plugd_proc_cb;
    plugd_proc.pid = fork();
    if (!plugd_proc.pid) {
        execvp(plug[0],plug); 
        ERROR("Failed to start plugd\n");
        exit(-1);
    }
    if (plugd_proc.pid <= 0) {
        ERROR("Failed to start new plugd instance\n");
        return;
    }
    uloop_process_add(&plugd_proc);

    setenv("PREINIT","1",1);

    preinit_proc.cb = spawn_procd;
    preinit_proc.pid = fork();
    if (!preinit_proc.pid) {
        execvp(init[0],init);
        ERROR("Failed to start preinit\n");
        exit(-1);
    }
    if (preinit_proc.pid <= 0) {
        ERROR("Failed to start new preinit instance\n");
        return;
    }
    uloop_process_add(&preinit_proc);

    DEBUG(4,"Launched preinit instance,pid=%d\n",(int) preinit_proc.pid);
}

在preinit函数中同时启动procd进程/sbin/procd,procd的执行代码如下:

int main(int argc,char **argv)
{
    int ch;
    char *dbglvl = getenv("DBGLVL");

    if (dbglvl) {
        debug = atoi(dbglvl);
        unsetenv("DBGLVL");
    }

    while ((ch = getopt(argc,argv,"d:s:h:")) != -1) {
        switch (ch) {
        case 'h':
            return hotplug_run(optarg);
        case 's':
            ubus_socket = optarg;
            break;
        case 'd':
            debug = atoi(optarg);
            break;
        default:
            return usage(argv[0]);
        }
    }
    uloop_init();
    procd_signal();
    trigger_init();
    if (getpid() != 1)
        procd_connect_ubus();//ubusd进程存在则连接ubus
    else
        procd_state_next(); //不存在则,启动ubusd进程
    uloop_run();

    return 0;
}

在函数procd_state_next中调用state_enter 启动ubusd进程

static void state_enter(void)
{
    char ubus_cmd[] = "/sbin/ubusd";

    switch (state) {
    case STATE_EARLY:
        LOG("- early -\n");
        watchdog_init(0);
        hotplug("/etc/hotplug.json");
        procd_coldplug();
        break;

    case STATE_INIT:
        // try to reopen incase the wdt was not available before coldplug
        watchdog_init(0);
        LOG("- ubus -\n");
        procd_connect_ubus();

        LOG("- init -\n");
        service_init();
        service_start_early("ubus",ubus_cmd);

        procd_inittab();
        procd_inittab_run("respawn");
        procd_inittab_run("askconsole");
        procd_inittab_run("askfirst");
        procd_inittab_run("sysinit");
        break;

    case STATE_RUNNING:
        LOG("- init complete -\n");
        break;

    case STATE_SHUTDOWN:
        LOG("- shutdown -\n");
        procd_inittab_run("shutdown");
        sync();
        break;

    case STATE_HALT:
        LOG("- reboot -\n");
        reboot(reboot_event);
        break;

    default:
        ERROR("Unhandled state %d\n",state);
        return;
    };
}

启动preinit时执行的/etc/preinit脚本,内容如下:

#!/bin/sh
# Copyright (C) 2006 OpenWrt.org
# Copyright (C) 2010 Vertical Communications

[ -z "$PREINIT" ] && exec /sbin/init

export PATH=/bin:/sbin:/usr/bin:/usr/sbin

pi_ifname=
pi_ip=192.168.1.1
pi_broadcast=192.168.1.255
pi_netmask=255.255.255.0

fs_failsafe_ifname=
fs_failsafe_ip=192.168.1.1
fs_failsafe_broadcast=192.168.1.255
fs_failsafe_netmask=255.255.255.0

fs_failsafe_wait_timeout=2

pi_suppress_stderr="y"
pi_init_suppress_stderr="y"
pi_init_path="/bin:/sbin:/usr/bin:/usr/sbin"
pi_init_cmd="/sbin/init"

. /lib/functions.sh
. /lib/functions/preinit.sh
. /lib/functions/system.sh

boot_hook_init preinit_essential
boot_hook_init preinit_main
boot_hook_init failsafe
boot_hook_init initramfs
boot_hook_init preinit_mount_root

for pi_source_file in /lib/preinit/*; do
        . $pi_source_file
done

boot_run_hook preinit_essential

pi_mount_skip_next=false
pi_jffs2_mount_success=false
pi_failsafe_net_message=false

boot_run_hook preinit_main

/etc/preinit脚本是一系列初始化脚本的入口,定义了初始化的各种参数,preinit的一系列脚本放在/lib/preinit/文件夹下:

02_default_set_state         50_indicate_regular_preinit
03_preinit_do_ralink.sh      70_initramfs_test
10_indicate_failsafe         80_mount_root
10_indicate_preinit          81_urandom_seed
10_sysinfo                   99_10_failsafe_login
30_failsafe_wait             99_10_run_init
40_run_failsafe_hook

由于脚本众多,因此openwrt的设计者将这些脚本分成下面几类:

preinit_essential
preinit_main
failsafe
initramfs
preinit_mount_root

每一类函数按照脚本的开头数字的顺序运行。

preinit执行的最后一个脚本为99_10_run_init,运行

exec env - PATH=$pi_init_path $pi_init_env $pi_init_cmd

pi_init_cmd

pi_init_cmd="/sbin/init"

因此开始运行busybox的init命令

busybox的init命令执行/etc/inittab的脚本,/etc/inittab 内容如下:

::sysinit:/etc/init.d/rcS S boot
::shutdown:/etc/init.d/rcS K stop
tts/0::askfirst:/bin/ash --login
ttyS0::askfirst:/bin/ash --login
tty1::askfirst:/bin/ash --login

sysinit为系统初始化运行的 /etc/init.d/rcS S boot脚本
shutdown为系统重启或关机运行的脚本
tty开头的是,如果用户通过串口或者telnet登录,则运行/bin/ash --login
askfirst和respawn相同,只是在运行前提示”Please press Enter to activate
this console.”

当前启动转到运行 /etc/init.d/rcS S boot,/etc/init.d/rcS和preinit类似,rcS也是一系列脚本的入口,其运行/etc/rc.d目录下S开头的的所
有脚本(如果运行rcS K stop,则运行K开头的所有脚本)

K50dropbear S02nvram S40network S50dropbear S96led
K90network S05netconfig S41wmacfixup S50telnet S97watchdog
K98boot S10boot S45firewall S60dnsmasq S98sysntpd
K99umount S39usb S50cron S95done S99sysctl

上面的脚本文件来自/etc/init.d/,在该文件夹下包含了各种应用程序的初始化脚本,这些脚本通过/etc/rc.common脚本,将init.d的脚
本链接到/etc/rc.d目录下,并且根据 这些脚本中的START和STOP的关键字,添加K${STOP}S${START}的前缀,这样就决定了脚本的先后的运行次序。

openwrt的shell脚本比较复杂,因此看脚本时可以通过添加set -xecho等命令,直接看shell脚本的结果,而不要花太多的时间硬看脚本,主要是理解其主要的意思和设计思路。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


用的openwrt路由器,家里宽带申请了动态公网ip,为了方便把2280端口映射到公网,发现经常被暴力破解,自己写了个临时封禁ip功能的脚本,实现5分钟内同一个ip登录密码错误10次就封禁这个ip5分钟,并且进行邮件通知使用步骤openwrt为19.07.03版本,其他版本没有测试过安装bashmsmtpopkg
#!/bin/bashcommand1&command2&wait从Shell脚本并行运行多个程序–杨河老李(kviccn.github.io)
1.先查出MAMP下面集成的PHP版本cd/Applications/MAMP/bin/phpls-ls 2.编辑修改.bash_profile文件(没有.bash_profile文件的情况下回自动创建)sudovim~/.bash_profile在文件的最后输入以下信息,然后保存退出exportPATH="/Applications/MAMP/bin/php/php7.2.20/b
1、先输入locale-a,查看一下现在已安装的语言2、若不存在如zh_CN之类的语言包,进行中文语言包装:apt-getinstalllanguage-pack-zh-hans3、安装好后我们可以进行临时修改:然后添加中文支持: locale-genzh_CN.UTF-8临时修改> export LC_ALL='zh_CN.utf8'> locale永久
BashPerlTclsyntaxdiff1.进制数表示Languagebinaryoctalhexadecimalbash2#[0~1]0[0~7]0x[0~f]or0X[0~f]perl0b[0~1]0[0~7]0x[0~f]tcl0b[0~1]0o[0~7]0x[0~f]bashdifferentbaserepresntationreference2.StringlengthLanguageStr
正常安装了k8s后,使用kubect工具后接的命令不能直接tab补全命令补全方法:yum-yinstallbash-completionsource/usr/share/bash-completion/bash_completionsource<(kubectlcompletionbash)echo"source<(kubectlcompletionbash)">>~/.bashrc 
参考这里启动jar包shell脚本修改过来的#!/bin/bash#默认应用名称defaultAppName='./gadmin'appName=''if[[$1&&$1!=0]]thenappName=$1elseappName=$defaultAppNamefiecho">>>>>>本次重启的应用:$appName<
#一个数字的行#!/bin/bashwhilereadlinedon=`echo$line|sed's/[^0-9]//g'|wc-L`if[$n-eq1]thenecho$linefidone<1.txt#日志切割归档#!/bin/bashcd/data/logslog=1.logmv_log(){[-f$1]&&mv$1$2
#文件增加内容#!/bin/bashn=0cat1.txt|whilereadlinedon=[$n+1]if[$n-eq5]thenecho$lineecho-e"#Thisisatestfile.\n#Testinsertlineintothisfile."elseecho$linefidone#备份/etc目录#
# su - oraclesu: /usr/bin/ksh: No such file or directory根据报错信息:显示无法找到文件 /usr/bin/ksh果然没有该文件,但是发现存在文件/bin/ksh,于是创建了一个软连接,可以规避问题,可以成功切换到用户下,但无法执行系统自带命令。$. .bash_profile-ksh: .: .b
history显示历史指令记录内容,下达历史纪录中的指令主要的使用方法如果你想禁用history,可以将HISTSIZE设置为0:#exportHISTSIZE=0使用HISTIGNORE忽略历史中的特定命令下面的例子,将忽略pwd、ls、ls-ltr等命令:#exportHISTIGNORE=”pwd:ls:ls-ltr:”使用HIS
一.命令历史  1.history环境变量:    HISTSIZE:输出的命令历史条数,如history的记录数    HISTFILESIZE:~/.bash_history保存的命令历史记录数    HISTFILLE:历史记录的文件路径    HISTCONTROL:     ignorespace:忽略以空格开头的命令
之前在网上看到很多师傅们总结的linux反弹shell的一些方法,为了更熟练的去运用这些技术,于是自己花精力查了很多资料去理解这些命令的含义,将研究的成果记录在这里,所谓的反弹shell,指的是我们在自己的机器上开启监听,然后在被攻击者的机器上发送连接请求去连接我们的机器,将被攻击者的she
BashOne-LinersExplained,PartI:Workingwithfileshttps://catonmat.net/bash-one-liners-explained-part-oneBashOne-LinersExplained,PartII:Workingwithstringshttps://catonmat.net/bash-one-liners-explained-part-twoBashOne-LinersExplained,PartII
Shell中变量的作用域:在当前Shell会话中使用,全局变量。在函数内部使用,局部变量。可以在其他Shell会话中使用,环境变量。局部变量:默认情况下函数内的变量也是全局变量#!/bin/bashfunctionfunc(){a=99}funcecho$a输出>>99为了让全局变量变成局部变量
1、多命令顺序执行;  命令1;命令2  多个命令顺序执行,命令之间没有任何逻辑联系&&  命令1&&命令2  逻辑与,当命令1正确执行,才会执行命令2||  命令1||命令2  逻辑或,当命令1执行不正确,才会执行命令2例如:ls;date;cd/home/lsx;pwd;who ddif=输入文件of=输
原博文使用Linux或者unix系统的同学可能都对#!这个符号并不陌生,但是你真的了解它吗?首先,这个符号(#!)的名称,叫做"Shebang"或者"Sha-bang"。Linux执行文件时发现这个格式,会把!后的内容提取出来拼接在脚本文件或路径之前,当作实际执行的命令。 Shebang这个符号通常在Unix系统的脚本
1、历史命令history[选项][历史命令保存文件]选项:-c:  清空历史命令-w:  把缓存中的历史命令写入历史命令保存文件 ~/.bash_historyvim/etc/profile中的Histsize可改存储历史命令数量历史命令的调用使用上、下箭头调用以前的历史命令使用“!n”重复执行第n条历史
目录1.Shell脚本规范2.Shell脚本执行3.Shell脚本变量3.1环境变量3.1.1自定义环境变量3.1.2显示与取消环境变量3.1.3环境变量初始化与对应文件的生效顺序3.2普通变量3.2.1定义本地变量3.2.2shell调用变量3.2.3grep调用变量3.2.4awk调用变量3.3
   http://www.voidcn.com/blog/wszzdanm/article/p-6145895.html命令功能:显示登录用户的信息命令格式:常用选项:举例:w显示已经登录的用户及正在进行的操作[root@localhost~]#w 11:22:01up4days,21:22, 3users, loadaverage:0.00,0.00,0.00USER