Android logwrapperによるlogリダイレクト


Androidにはprintfなどの標準関数で出力されるアプリケーションのログ出力がありますが、このようなログは記録できません.主にinitプロセスは0,1,2の3つの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
"
, line); } }

親プロセスは、サブプロセスから出力されたlogを受け取り、他の方法で出力するlogリダイレクトの役割を果たします.上記の実装から、親プロセスはdo_log_ラインは3つの方法で出力されます.
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
"
); 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
"
); 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
"
); rc = -1; goto err_ptty; } child_ptty = TEMP_FAILURE_RETRY(open(child_devname, O_RDWR)); if (child_ptty < 0) { ERROR("Cannot open child_ptty
"
); 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
"
); 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