Linux kernel에서 usb-storage 등을 꽂으면 자동으로 인식되어 mount까지 되는 것을 볼 수 있는데, 이것을 hotplug라고 한다.  Hotplug가 일어나는 과정은 대략 다음과 같이 기술할 수 있다.

1. 장비 꽂음

2. Kernel의 interrupt routine을 거친 후 kobject에서 인식

3. uevent가 발생하고 user application인 hotplug routine 실행

따라서 이러한 hotplug signal을 받아낼 수 있다면 새로운 장치가 인식되었을 때에 원하는 작업을 수행할 수 있게 된다.  이 문서에서는 이를 가능하게 하는 방법을 기술하는 것을 목표로 한다.

Kernel 2.6.17에서 uevent에 관련된 정보는 lib/kobject_uevent.c에서 찾아볼 수 있다.

void kobject_uevent (struct kobject *kobj, enum kobject_action action)

{

  char **envp;

  char *buffer;

  char *scratch;

  const char *action_string;

  const char *devpath = NULL;

  const char *subsystem;


  ...

 

  /* call uevent_helper, usually only enabled during early boot */

  if (uevent_helper[0]) {

    char *argv[3];

  

    argv[0] = uevent_helper;

    argv[1] = (char *) subsystem;

    argv[2] = NULL;

    call_usermodehelper (argv[0], argv, envp, 0);

    /* send signal to detect daemon routine end */.

  }

  

  ...

}


모든 routine이 실행되고 마지막으로 call_usermodehelper ()를 호출하여 user 영역의 application을 실행한다.  현재 uevent_helper는 “/sbin/hotplug”이다.

따라서, signal을 받기 원하는 daemon에게 이러한 signal을 전달해줄 user application을 작성하고, 이 uevent_helper에 등록하면 실행될 것이다.  여기서는 이러한 user application이 /root/detect_test라고 가정하도록 하겠다.

...


  /* call uvent_helper, usually only enabled during early boot */

  if (uevent_helper[0]) {

    char *argv[3];


    argv[0] = uevent_helper;

    argv[1] = (char *) subsystem;

    argv[2] = NULL;

    call_usermodehelper (argv[0], argv, envp, 0);


    /* edit part start */

    if (0 == strcmp (“mount”, action_string) || 0 == strcmp (“unmount”, action_string)) {

      argv[0] = “/root/detect_test”;

      call_usermodehelper (argv[0], argv, envp, 0);

    }

    /* edit part end */


    /* send signal to detect daemon routine end */

  }

  

  ...

}


이렇게 작성한다면 장비를 꽂거나 제거할 때에 /root/detect_test라는 user application이 호출된다.  이때 /root/detect_test에 전달된 argv와 envp를 사용하면 여러가지 정보를 알 수 있다.

/root/detect_test

block


HOME=/

PATH=/sbin;/bin;/usr/sbin;/usr/bin;

ACTION=mount

DEVPATH=/block/sda/sda1

SUBSYSTEM=block

SEQNUM=706

MINOR=1

MAJOR=8

PHYSDEVPATH=/devices/platform/s3c2410-ohci/usb1/1-1/1-1:1.0/host0/target0:0:0/0:0:0:0

PHYSDEVBUS=scsi

PHYSDEVDRIVER=sd


2010년 3월 10일에 추가함
============================================
2.6.23.17 kernel에서 확인한 결과, user helper를 사용하지 않고도 kernel과 user app을 연결시키는 방법이 아래와 같다.

   
    int ns = -1;
    struct sockaddr_nl sa;
    char buf[1024];
    struct iovec iov = { buf, sizeof(buf) };
    struct msghdr msg = { &sa, sizeof(sa),&iov, 1, NULL, 0, 0};
    int len;

    (void)params;

    memset(&sa, 0, sizeof(sa));
    sa.nl_family = AF_NETLINK;
    sa.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR;

    ns = socket(AF_NETLINK, SOCK_RAW, NETLINK_KOBJECT_UEVENT);
    if(ns < 0)
    {
        PrintError("[%s]%d socket()",__func__,__LINE__);
        return;
    }

    if(bind(ns, (struct sockaddr *)&sa, sizeof(sa)))
    {
        PrintError("[%s]%d bind()",__func__,__LINE__);
        close(ns);
        return;
    }

    while(1) {
        len = recvmsg(ns, &msg, 0);
        ...
    }

    close(ns);

RAW socket을 이용하여 recv가 가능하구나... 이제야 알았다... -.-;;

Posted by 강군님

댓글을 달아 주세요

  1. Sunny 2012.02.13 11:39  댓글주소  수정/삭제  댓글쓰기

    올려주신 post잘 보았습니다. uevent_helper를 사용하지 않고 socket으로 kobject_uevent를 받아서 처리해보려고 하는데, 막상 recvmsg()까지 와서 kobject까지 얻어내는 방안이 묘연합니다.
    예를 들어 usb가 꽂혔을 때 uevent가 발생하고 난 후 "/sbin/hotplug"를 실행시키고자 하는데,(물론 default로 들어가있긴하지만 만약 없다고 가정하면) 이를 실행시키려면 subsystem을 넣어줘야 다음과 같이 호출이 가능합니다.

    call_usermodehelper ("/sbin/hotplug", (char *)subsystem, NULL, 0); (linux/lib/kobject_uevent.c참고)
    여기서 subsystem은 kobject로부터 받아오게 되는데요.
    recvmsg로 uevent를 받고서 msghdr로부터 kobject를 받아와야 되는걸로 보이는데, 방법이 잘 찾아지지 않네요. .ㅠ.ㅠ 도움 부탁드립니다

  2. Favicon of https://kangun.tistory.com 강군님 2012.02.15 10:09 신고  댓글주소  수정/삭제  댓글쓰기

    출장지라서 답글을 늦게 보았습니다 ^^;; 그런데 무엇을 원하시는 것인지요?

    지금 올리신 내용으로는 잘 이해가 안되서요.. 알고 계시겠지만 위쪽에 있는 code는 kernel space에서의 것이고, 아래쪽에 새로 넣은 것은 user space에서의 code입니다.

    무엇을 원하시는 것인지 잘 이해를 못해서... 뭐라고 말씀드리기가 힘드네요 ^^

  3. Sunny 2012.02.17 15:40  댓글주소  수정/삭제  댓글쓰기

    안녕하세요. 답글 감사드립니다.
    제가 두 개의 구현 방법을 참고삼아, 보던중에, 실제 구현은 아래 user space의 code를 작업하던 중이었는데, 하다가 막히는 바람에 글을 올리게 되었습니다. 실제 kernel space에서 작업을 하게 되면 관련 환경 변수들(envp)이 넘어가게 되니까 문제가 없는데, user space에서 recvmsg로 받고 난 후에 이 envp를 얻는 것에서 막혔습니다. 실제 envp가 넘어가질 않으니 제가 user space에서 user event를 받아도 제대로 처리가 되지 않더군요.

    아 참고로 제가 작업하데 된 background는 proc/sys/kernel/hotplug에 sbin/hotplug를 넣지 않는 상황에서 user event를 받아 이 sbin/hotplug를 trigger하려는 것입니다.

  4. Favicon of https://kangun.tistory.com 강군님 2012.03.14 09:20 신고  댓글주소  수정/삭제  댓글쓰기

    헐~ 답글이 굉장히 많이 늦어졌네요.. ^^;;; 아직도 출장지에 있습니다.. ㅎㅎ 집이 그립네요 -.-

    실제로 말씀하신 것처럼 envp가 넘어오지는 않습니다만, 넘어온 정보들로 sdXn인지 block device인지 등등을 알 수 있습니다. 넘겨진 정보들을 사용하여 /sys의 값들을 활용하여도 찾을 수가 있고요..

    다만 kernel space에서 바로 넘겨준 것처럼 보기 쉽게(?) 정리가 되지는 않긴 하네요.. 방법을 한번 찾아봐야겠군요.. ㅎㅎ (이글을 보실지도 의문입니다만... ^^)