android使用logwrapper进行log重定向

在Android有一些应用程序的日志输出是通过printf之类的标准函数输出的,这类log是无法记录到的。主要是由于init进程会把0,1,2三个fd指向到/dev/null,而其他进程都是由init fork出来的,所以标准输出和标准错误输出都会继承自父进程,所以默认也都是不打印出来的。

android init的实现在system/core/init/init.c中:


int main(int argc, char** argv) {

......


 // At this point we're in the second stage of init.

 InitKernelLogging(argv);

 LOG(INFO) << "init second stage started!";

......
}

init会执行log初始化动作,也就是把所有的标准输入标准输出和标准错误输出都指向/dev/null:


void InitKernelLogging(char* argv[]) {

    // Make stdin/stdout/stderr all point to /dev/null.

    int fd = open("/sys/fs/selinux/null", O_RDWR);

    if (fd == -1) {

        int saved_errno = errno;

        android::base::InitLogging(argv, &android::base::KernelLogger);

        errno = saved_errno;

        PLOG(FATAL) << "Couldn't open /sys/fs/selinux/null";

    }

    dup2(fd, 0);

    dup2(fd, 1);

    dup2(fd, 2);

    if (fd > 2) close(fd);



    android::base::InitLogging(argv, &android::base::KernelLogger);

}

android中提供了logwrapper程序用来重定向log的输出,重定向的log可以使用logcat查看,我们来看下他的实现机制又是怎样的呢?

logwrapper的源代码实现在system/core/logwrapper中,原理如下:

通过logwrapper的封装来执行一个command,logwrapper会fork一个子进程,并在子进程中exec执行cmd,父进程负责和子进程通讯,并记录子进程输出的log,父进程会根据logwrapper的参数来选择对应的log输出方式:


/* Log directly to the specified log */

static void do_log_line(struct log_info *log_info, char *line) {

    if (log_info->log_target & LOG_KLOG) {

        klog_write(6, log_info->klog_fmt, line);

    }

    if (log_info->log_target & LOG_ALOG) {

        ALOG(LOG_INFO, log_info->btag, "%s", line);

    }

    if (log_info->log_target & LOG_FILE) {

        fprintf(log_info->fp, "%s\n", line);

    }

}

父进程起到一个log重定向的作用,它收取子进程输出的log,并通过其他方式输出出来,从上面的一段实现上可以看出,父进程可以通过do_log_line来按照三种方式来输出:

1)LOG_KLOG的方式输出,调用的是klog_write来输出kernel log2)LOG_ALOG,这种是输出给logcat使用的log,默认是LOG INFO级别

(3)LOG_FILE,输出log到指定file文件,在我看得这个版本代码中,此方法暂时还不可用

父进程和子进程采用的是ptty机制来做进程通讯的,具体实现在system/core/logwrapper/logwrap.c中:


int android_fork_execvp_ext(int argc, char* argv[], int *status, bool ignore_int_quit,

        int log_target, bool abbreviated, char *file_path,

        void *unused_opts, int unused_opts_len) {

    pid_t pid;

    int parent_ptty;

    int child_ptty;

    struct sigaction intact;

    struct sigaction quitact;

    sigset_t blockset;

    sigset_t oldset;

    int rc = 0;



    LOG_ALWAYS_FATAL_IF(unused_opts != NULL);

    LOG_ALWAYS_FATAL_IF(unused_opts_len != 0);



    rc = pthread_mutex_lock(&fd_mutex);

    if (rc) {

        ERROR("failed to lock signal_fd mutex\n");

        goto err_lock;

    }



    /* Use ptty instead of socketpair so that STDOUT is not buffered */          //使用ptty代替socket,因为socket是有缓冲的

    parent_ptty = TEMP_FAILURE_RETRY(open("/dev/ptmx", O_RDWR));

    if (parent_ptty < 0) {

        ERROR("Cannot create parent ptty\n");

        rc = -1;

        goto err_open;

    }



    char child_devname[64];

    if (grantpt(parent_ptty) || unlockpt(parent_ptty) ||

            ptsname_r(parent_ptty, child_devname, sizeof(child_devname)) != 0) {

        ERROR("Problem with /dev/ptmx\n");

        rc = -1;

        goto err_ptty;

    }



    child_ptty = TEMP_FAILURE_RETRY(open(child_devname, O_RDWR));

    if (child_ptty < 0) {

        ERROR("Cannot open child_ptty\n");

        rc = -1;

        goto err_child_ptty;

    }



    sigemptyset(&blockset);

    sigaddset(&blockset, SIGINT);

    sigaddset(&blockset, SIGQUIT);

    pthread_sigmask(SIG_BLOCK, &blockset, &oldset);



    pid = fork();

    if (pid < 0) {

        close(child_ptty);

        ERROR("Failed to fork\n");

        rc = -1;

        goto err_fork;

    } else if (pid == 0) {

        pthread_mutex_unlock(&fd_mutex);

        pthread_sigmask(SIG_SETMASK, &oldset, NULL);

        close(parent_ptty);                                       //创建的子进程会继承父进程的fd,所以要关闭不需要的fd



        dup2(child_ptty, 1);

        dup2(child_ptty, 2);

        close(child_ptty);



        child(argc, argv);

    } else {

        close(child_ptty);                                            //父进程也要关闭不需要的fd,剩余的fd就是用于进程交互的fd了

        if (ignore_int_quit) {

            struct sigaction ignact;



            memset(&ignact, 0, sizeof(ignact));

            ignact.sa_handler = SIG_IGN;

            sigaction(SIGINT, &ignact, &intact);

            sigaction(SIGQUIT, &ignact, &quitact);

        }



        rc = parent(argv[0], parent_ptty, pid, status, log_target,

                    abbreviated, file_path);

    }



    if (ignore_int_quit) {

        sigaction(SIGINT, &intact, NULL);

        sigaction(SIGQUIT, &quitact, NULL);

    }

err_fork:

    pthread_sigmask(SIG_SETMASK, &oldset, NULL);

err_child_ptty:

err_ptty:

    close(parent_ptty);

err_open:

    pthread_mutex_unlock(&fd_mutex);

err_lock:

    return rc;

}

示例:


servcie akmd /system/bin/logwrapper /sbin/akmd

你可能感兴趣的:(logwrapper,log,android,log,ALOG)