注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

Tsecer的回音岛

Tsecer的博客

 
 
 

日志

 
 

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

2012-02-19 21:41:46|  分类: Busybox |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
一、回顾一下
之前是说,对于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
  评论这张
 
阅读(835)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017