Android logwrapperによるlogリダイレクト
9177 ワード
Androidにはprintfなどの標準関数で出力されるアプリケーションのログ出力がありますが、このようなログは記録できません.主にinitプロセスは0,1,2の3つのfdを/dev/nullに指向し、他のプロセスはinit forkから出るため、標準出力と標準エラー出力は親プロセスから継承されるため、デフォルトでは印刷されません.
android initの実現はsystem/core/init/initである.c中:
Initはlog初期化動作を実行します.つまり、すべての標準入力標準出力と標準エラー出力を/dev/nullに指します.
Androidではlogwrapperプログラムがlogの出力をリダイレクトするために提供されています.リダイレクトしたlogはlogcatで見ることができますが、彼の実現メカニズムはどうなっていますか.
logwrapperのソースコードはsystem/core/logwrapperで実現され、原理は以下の通りである.
logwrapperのパッケージを使用してcommandを実行します.logwrapperはサブプロセスをforkし、サブプロセスでexecはcmdを実行します.親プロセスはサブプロセスと通信し、サブプロセスから出力されたlogを記録します.親プロセスはlogwrapperのパラメータに基づいて対応するlog出力方式を選択します.
親プロセスは、サブプロセスから出力されたlogを受け取り、他の方法で出力するlogリダイレクトの役割を果たします.上記の実装から、親プロセスはdo_log_ラインは3つの方法で出力されます.
親プロセスとサブプロセスはpttyメカニズムを採用してプロセス通信を行い、具体的にはsystem/core/logwrapper/logwrapを実現する.c中:
例:
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 log
(2)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