2007年4月24日火曜日

udevd main

udevdのmain関数の解析メモ

int main(int argc, char *argv[], char *envp[])
{
    int retval;
    int fd;
    struct sigaction act;
    fd_set readfds;
    const char *value;
    int daemonize = 0;
    int option;
    static const struct option options[] = {
        { "daemon", 0, NULL, 'd' },
        { "debug-trace", 0, NULL, 't' },
        { "verbose", 0, NULL, 'v' },
        { "help", 0, NULL, 'h' },
        {}
    };
    int rc = 1;
    int maxfd;

    logging_init("udevd");
    udev_config_init();  環境変数からconfig file名などを取得。
    selinux_init();  何もしない
    dbg("version %s", UDEV_VERSION);

    /* parse commandline options */
    while (1) {
        コマンドオプションの解析(省略)
    }

    rootで実行されているかチェック
    if (getuid() != 0) {
        fprintf(stderr, "root privileges required\n");
        err("root privileges required");
        goto exit;
    }

    /* init sockets to receive events */
    制御用のUNIXドメインソケットを作成
    if (init_udevd_socket() < 0) {
        if (errno == EADDRINUSE) {
            fprintf(stderr, "another udev daemon already running\n");
            err("another udev daemon already running");
            rc = 1;
        } else {
            fprintf(stderr, "error initializing udevd socket\n");
            err("error initializing udevd socket");
            rc = 2;
        }
        goto exit;
    }
    カーネルイベント用のソケット作成
    if (init_uevent_netlink_sock() < 0) {
        fprintf(stderr, "error initializing netlink socket\n");
        err("error initializing netlink socket");
        rc = 3;
        goto exit;
    }

    /* setup signal handler pipe */
    シグナルハンドラーからの通知に使用するパイプを作成
    retval = pipe(signal_pipe);
    if (retval < 0) {
        err("error getting pipes: %s", strerror(errno));
        goto exit;
    }

    作成したpipeに対してfcntlが可能かどうかチェックしNONBLOCKに設定する
    retval = fcntl(signal_pipe[READ_END], F_GETFL, 0);
    if (retval < 0) {
        err("error fcntl on read pipe: %s", strerror(errno));
        goto exit;
    }
    retval = fcntl(signal_pipe[READ_END], F_SETFL, retval | O_NONBLOCK);
    if (retval < 0) {
        err("error fcntl on read pipe: %s", strerror(errno));
        goto exit;
    }

    retval = fcntl(signal_pipe[WRITE_END], F_GETFL, 0);
    if (retval < 0) {
        err("error fcntl on write pipe: %s", strerror(errno));
        goto exit;
    }
    retval = fcntl(signal_pipe[WRITE_END], F_SETFL, retval | O_NONBLOCK);
    if (retval < 0) {
        err("error fcntl on write pipe: %s", strerror(errno));
        goto exit;
    }

    ルールファイルをパースしてメモリに展開する。
    /* parse the rules and keep them in memory */
    sysfs_init();
    udev_rules_init(&rules, 1);

    export_initial_seqnum();

    デーモン化するために子プロセスを作成し親は終了する
    if (daemonize) {
        pid_t pid;

        pid = fork();
        switch (pid) {
        case 0:
            dbg("daemonized fork running");
            break;
        case -1:
            err("fork of daemon failed: %s", strerror(errno));
            rc = 4;
            goto exit;
        default:
            dbg("child [%u] running, parent exits", pid);
            rc = 0;
            goto exit;
        }
    }
    /dev/nullを標準入力と標準出力に設定
    /* redirect std fd's */
    fd = open("/dev/null", O_RDWR);
    if (fd >= 0) {
        dup2(fd, STDIN_FILENO);
        if (!verbose)
            dup2(fd, STDOUT_FILENO);
        dup2(fd, STDERR_FILENO);
        if (fd > STDERR_FILENO)
            close(fd);
    } else
        err("error opening /dev/null: %s", strerror(errno));

    /* set scheduling priority for the daemon */
    setpriority(PRIO_PROCESS, 0, UDEVD_PRIORITY);

    chdir("/");
    umask(022);

    /* become session leader */
    セッションリーダーになる
    sid = setsid();
    dbg("our session is %d", sid);

    /* OOM_DISABLE == -17 */
    Out Of Memory Killerを無効にするために/proc/self/oom_adに-17を書く
    OOM Killerについては詳細記事を参照のこと

    fd = open("/proc/self/oom_adj", O_RDWR);
    if (fd < 0)
        err("error disabling OOM: %s", strerror(errno));
    else {
        write(fd, "-17", 3);
        close(fd);
    }

    シグナルハンドラーを設定する
    /* set signal handlers */
    memset(&act, 0x00, sizeof(struct sigaction));
    act.sa_handler = (void (*)(int)) sig_handler;
    sigemptyset(&act.sa_mask);
    act.sa_flags = SA_RESTART;
    sigaction(SIGINT, &act, NULL);
    sigaction(SIGTERM, &act, NULL);
    sigaction(SIGCHLD, &act, NULL);
    sigaction(SIGHUP, &act, NULL);

    /* watch rules directory */
    ルールディレクトリィの監視を行う。glibc経由ではなくsyscallで直接呼ぶ。
    syscall(__NR_inotify_init),syscall(__NR_inotify_add_watch,・・・など

    inotify_fd = inotify_init();
    if (inotify_fd >= 0)
        inotify_add_watch(inotify_fd, udev_rules_dir, IN_CREATE | IN_DELETE | IN_MOVE | IN_CLOSE_WRITE);
    else if (errno == ENOSYS)
        err("the kernel does not support inotify, udevd can't monitor configuration file changes");
    else
        err("inotify_init failed: %s", strerror(errno));

    /* maximum limit of forked childs */
    子プロセス数の設定値を取得(省略)

    /* export log_priority , as called programs may want to follow that setting */
    sprintf(udev_log, "UDEV_LOG=%i", udev_log_priority);
    putenv(udev_log);
    if (debug_trace)
        putenv("DEBUG=1");

    select用ファイルディスクリプタの最大値を取得
    maxfd = udevd_sock;
    maxfd = UDEV_MAX(maxfd, uevent_netlink_sock);
    maxfd = UDEV_MAX(maxfd, signal_pipe[READ_END]);
    maxfd = UDEV_MAX(maxfd, inotify_fd);

    selectでイベント受信待ち
    while (!udev_exit) {
        struct udevd_uevent_msg *msg;
        int fdcount;

        FD_ZERO(&readfds);
        FD_SET(signal_pipe[READ_END], &readfds);
        FD_SET(udevd_sock, &readfds);
        FD_SET(uevent_netlink_sock, &readfds);
        if (inotify_fd >= 0)
            FD_SET(inotify_fd, &readfds);

        fdcount = select(maxfd+1, &readfds, NULL, NULL, NULL);
        if (fdcount < 0) {
            if (errno != EINTR)
                err("error in select: %s", strerror(errno));
            continue;
        }

            イベントごとの処理を行う。(省略)

    }
    rc = 0;

    後始末を行う
exit:
    udev_rules_cleanup(&rules);
    sysfs_cleanup();

    if (signal_pipe[READ_END] >= 0)
        close(signal_pipe[READ_END]);
    if (signal_pipe[WRITE_END] >= 0)
        close(signal_pipe[WRITE_END]);

    if (udevd_sock >= 0)
        close(udevd_sock);
    if (inotify_fd >= 0)
        close(inotify_fd);
    if (uevent_netlink_sock >= 0)
        close(uevent_netlink_sock);

    logging_close();

    return rc;
}

■ 参考資料

JM Manpage of PIPE
JM Manpage of FCNTL
JM Manpage of SETSID
JM Manpage of SIGACTION

0 件のコメント: