交互shell设置为/dev/console之后提示job control turned off(中)

一、回顾一下
之前是说,对于etc/inittab中配置项,如果在执行的交互式shell命令前没有添加‘-’的话,那么系统启动的时候,shell会提示
job control turned off
,这个提示可能乍一看不知道是啥意思,反正惊出一身冷汗还是妥妥的。这里的输出的意思就是shell没有找到自己的控制终端,而这个控制终端一般是session leader打开第一个tty设备的时候由内核自动设置的,但是偏偏内核对这个/dev/console(准确的说是主设备号为5,次设备号为1的文件设备)有严重的偏见,那就是session leader打开这个设备的时候不会把它设置为控制终端,内核中的代码为:
linux-2.6.21\drivers\char\tty_io.c
static int tty_open(struct inode * inode, struct file * filp)
    if (device == MKDEV(TTYAUX_MAJOR,1)) {
        driver = console_device(&index);
        if (driver) {
            /* Don't let /dev/console block */
            filp->f_flags |= O_NONBLOCK;
             noctty = 1 ;
            goto got_driver;
        }
        mutex_unlock(&tty_mutex);
        return -ENODEV;
    }
所以导致init中派生的shell依然是没有控制终端这个主心骨。
二、妖风挡不住
本以为这个东西设置了之后就天下大吉,他们从此过上快乐的生活,但是最后还是发现有一个问题,那就是在busybox中执行一些命令的时候出现问题,Well,具体的说是在bb_getpass函数执行的时候出问题。当然这个问题依然是比我在这里说明的复杂的多,我是在这个函数里面每个函数调用(不管是系统调用还是库函数调用,每个函数前后加printf打印,这是最为原始的调试方法,但是你又不得不承认,很多时候,这也是最为有效的方法。就像密码的暴力破解一样,只要有时间,依然是最为犀利的方法)。最后发现bb_askpass函数中read系统调用返回值为零,也就是没有读到数据。案发第一现场为bb_askpass.c中
    while (1) {
        int r = read(fd, &ret[i], 1); 这里返回值为0,打印错误码为#define    EAGAIN        11    /* Try again */
        if (r < 0) {
            /* read is interrupted by timeout or ^C */
            ret = NULL;
            break;
        }
直观上感觉,返回EAGAIN一般是由于文件设置了O_NONBLOCK属性。仔细一看,果不其然就是这样。在第一章的分析过程中,我们只注意到了其中noctty=1这个语句,而忽略了之前那个同样咬人狗命的
filp->f_flags |= O_NONBLOCK ;
语句,直接是把这个设备设置为了非阻塞,杠杠滴,不含糊。
三、如何解决
在busybox的init.c中,其中对于run函数的处理为
static pid_t run(const struct init_action *a)
    if (!open_stdio_to_tty(a->terminal))
然后进一步实现为

/* Open the new terminal device.
 * NB: careful, we can be called after vfork! */
static int open_stdio_to_tty(const char* tty_name)
{
    /* empty tty_name means "use init's tty", else... */
    if (tty_name[0]) { 如果inittab中配置项没有在最开始指明使用哪个终端,也就是终端名为空,那么这个if不满足,进而会跳过里面的device_open函数的执行以及接下来的两个dup2
        int fd;

        close(STDIN_FILENO);
        /* fd can be only < 0 or 0: */
        fd = device_open(tty_name, O_RDWR);
        if (fd) {
            message(L_LOG | L_CONSOLE, "can't open %s: %s",
                tty_name, strerror(errno));
            return 0; /* failure */
        }
        dup2(STDIN_FILENO, STDOUT_FILENO);
        dup2(STDIN_FILENO, STDERR_FILENO);
    }
    set_sane_term();
    return 1; /* success */
}
device_open函数的代码很重要,如果他的参数中没有指明O_NONBLOCK属性,那么它会体贴的将设备文件设置为阻塞模式,而这个正式我们希望的效果。因为之前已经列了很多代码了,所以这里就不摘抄这个函数了。总之,大家知道是可以在这里把init的标准输入设置为阻塞就好了。
所以这里就需要满足open_stdio_to_tty中if (tty_name[0]) 这个条件,如何满足呢,就是我们再主动的把这个设备显式的,冗余的指明为console,从而让它把shell的标准输入、输出和错误设置为阻塞模式。所以这个启动列变化为
console::respawn:-/bin/sh
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值