Yongbing's Blog

A personal technical note.

Misc Code Samples

| Comments

1 inotify: monitoring hidraw device file add/remove events:

(inotify-example.c) download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
/*
 * Copyright (C) 2005 The Android Open Source Project
 * Copyright (C) 2013 Yongbing Chen <yongbing.chen.wh@gmail.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <poll.h>
#include <sys/epoll.h>
#include <sys/inotify.h>
#include <pthread.h>
#include <unistd.h>

static const unsigned int EPOLL_ID_INOTIFY = 0x80000001;
#define MONITORING_DIR "/dev"
#define HIDRAW_DEV_FILE_PREFIX "hidraw"

static int init_epoll_fd()
{
  int epoll_fd = epoll_create(1);
  if (epoll_fd < 0){
      printf("epoll_crate failed %s",strerror(errno));
      return -1;
  }
  return epoll_fd;
}

static int init_inotify(char * dev_path, int epoll_fd)
{
  int inotify_fd = inotify_init();
  if(inotify_fd < 0){
      printf("inotify_init failed, errno %s",strerror(errno));
      return -1;
  }

  int result = inotify_add_watch(inotify_fd, dev_path, IN_DELETE | IN_CREATE);
  if(result < 0){
      printf("Could not register INotify for %s  errno %s", dev_path, strerror(errno));
      close(inotify_fd);
      return -1;
  }
  return inotify_fd;
}

static int add_inotify_to_epoll(int epoll_fd, int inotify_fd)
{
  struct epoll_event event_item;
  memset(&event_item, 0, sizeof(event_item));
  event_item.events = EPOLLIN;
  event_item.data.u32 = EPOLL_ID_INOTIFY;
  int result = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, inotify_fd, &event_item);
  if (result != 0){
      printf("Could not add INotify to epoll instance.  errno %s", strerror(errno));
      return -1;
  }
  return 0;
}

static void process_add_event(char *hidraw_file_name)
{
  printf("%s, %s\n", __func__, hidraw_file_name);
  //may add the file to epoll here.
}

static void process_remove_event(char *hidraw_file_name)
{
  printf("%s, %s\n", __func__, hidraw_file_name);
  //may remove the file from epoll here.
}

//Cite from Android EventHub.cpp
static int read_inotify(int inotify_fd)
{
    int rc;
    char devname[128];
    char *filename;
    char event_buf[512];
    int event_size;
    int event_pos = 0;
    struct inotify_event *event;

    printf("read_inotify fd: %d\n", inotify_fd);
    rc = TEMP_FAILURE_RETRY(read(inotify_fd, event_buf, sizeof(event_buf)));
    if(rc < (int)sizeof(*event)) {
        printf("could not get event, %s\n", strerror(errno));
        return -1;
    }

    strcpy(devname, MONITORING_DIR);
    filename = devname + strlen(devname);
    *filename++ = '/';

    while(rc >= (int)sizeof(*event)) {
        event = (struct inotify_event *)(event_buf + event_pos);
        printf("%d: %08x \"%s\"\n", event->wd, event->mask, event->len ? event->name : "");
        if(event->len && !strncmp(event->name, HIDRAW_DEV_FILE_PREFIX, strlen(HIDRAW_DEV_FILE_PREFIX))) {
            strcpy(filename, event->name);
            if(event->mask & IN_CREATE) {
                process_add_event(devname);
            } else {
                process_remove_event(devname);
            }
        }
        event_size = sizeof(*event) + event->len;
        rc -= event_size;
        event_pos += event_size;
    }
    return 0;
}

static void process_inode_event(struct epoll_event *events,
      int event_counts, int inotify_fd)
{
  int i;
  for (i = 0; i < event_counts; i++){
      if (events->data.u32 == EPOLL_ID_INOTIFY){
          if (events->events & EPOLLIN) {
              read_inotify(inotify_fd);
          } else {
              printf("received unexpected epoll event 0x%08x for inotify.", events->events);
          }
      }
      events++;
  }
}

int main(int argc, char **argv)
{
  int epoll_fd = init_epoll_fd();
  if (epoll_fd < 0)
      return EXIT_FAILURE;

  int inotify_fd = init_inotify(MONITORING_DIR, epoll_fd);
  if (inotify_fd < 0){
      close(epoll_fd);
      return EXIT_FAILURE;
  }

  int rc = add_inotify_to_epoll(epoll_fd, inotify_fd);
  if (rc < 0){
      close(epoll_fd);close(inotify_fd);
      return EXIT_FAILURE;
  }

  static const int EPOLL_MAX_EVENTS = 16;
  struct epoll_event epoll_events[EPOLL_MAX_EVENTS];
  while(1){
      int rc = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, epoll_events, EPOLL_MAX_EVENTS, -1));
      if (rc <= 0) {
          printf("poll failed errno=%s\n", strerror(errno));
          return 1;
      } else {
          process_inode_event(epoll_events, rc, inotify_fd);
      }
  }
  close(inotify_fd);
  close(epoll_fd);

  return EXIT_SUCCESS;
 }

#ifdef __RUNNING_LOG
 130|shell@android:/data/app # ./test_inotify
[ 4998.540741] usb 1-1: USB disconnect, device number 3
read_inotify fd: 4
1: 00000200 "hidraw0"
Removing device '/dev/hidraw0' due to inotify event
process_remove_event, /dev/hidraw0
read_inotify fd: 4
1: 00000200 "hidraw1"
Removing device '/dev/hidraw1' due to inotify event
process_remove_event, /dev/hidraw1
[ 5002.247269] usb 1-1: new full-speed USB device number 4 using berlin-ehci
[ 5002.406589] usb 1-1: New USB device found, idVendor=046d, idProduct=c52b
[ 5002.413862] usb 1-1: New USB device strings: Mfr=1, Product=2, SerialNumber=0
[ 5002.421457] usb 1-1: Product: USB Receiver
[ 5002.425927] usb 1-1: Manufacturer: Logitech
read_inotify fd:[ 5002.443452] logitech-djreceiver 0003:046D:C52B.000B: hiddev0,hidraw0: USB HID v1.11 Device [Logitech USB Receiver] on usb-f7ed0000.usb-1/input2
 4
1: 00000100 "hidraw0"
process_add_event, /dev/hidraw0
[ 5002.464392] input: Logitech Unifying Device. Wireless PID:2012 as /devices/soc.0/f7ed0000.usb/usb1/1-1/1-1:1.2/0003:046D:C52B.000B/input/input4
read_inotify fd:[ 5002.479144] logitech-djdevice 0003:046D:C52B.000C: input,hidraw1: USB HID v1.11 Keyboard [Logitech Unifying Device. Wireless PID:2012] on usb-f7ed0000.usb-1:1
 4
1: 00000100 "hidraw1"
process_add_event, /dev/hidraw1
#endif

2 pthread timer_create: periodically generate an event, as a heartbeat:

(pthread_timer-example.c) download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
/*
 * Copyright (C) 2013 Yongbing Chen <yongbing.chen.wh@gmail.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <pthread.h>
#include <time.h>


typedef struct wrapper_class{
  int is_alarm_fired;
  timer_t timerid;
}wrapper_class, *wrapper_class_t;


static void something_is_on_fire(wrapper_class_t p)
{
  printf("%s, is_alarm_fired %d\n", __func__, p->is_alarm_fired);
}

static void defuse_the_fire_alarm(wrapper_class_t p)
{
  printf("%s, is_alarm_fired %d\n", __func__, p->is_alarm_fired);
}

static void trigger_alarm(union sigval sigev_val)
{
  wrapper_class_t p = (wrapper_class_t)sigev_val.sival_ptr;
  //TODO: may need mutex to protect object p.
  if (p->is_alarm_fired){
      something_is_on_fire(p);
  }
}

static void start_timer_for_alarm(wrapper_class_t p)
{
  struct sigevent sev;
  memset(&sev, 0, sizeof( struct sigevent));
  sev.sigev_notify = SIGEV_THREAD;
  sev.sigev_notify_function = trigger_alarm;
  sev.sigev_value.sival_ptr = p;
  if (timer_create(CLOCK_REALTIME, &sev, &p->timerid) == -1){
      return;
  }

  printf("timer ID is 0x%x\n", p->timerid);

  struct itimerspec its;
  its.it_value.tv_sec = 10;
  its.it_value.tv_nsec = 0;
  its.it_interval.tv_sec = 10;
  its.it_interval.tv_nsec = 0;
  if (timer_settime(p->timerid, 0, &its, NULL) == -1){
      printf("timer_settime failed, err %s\n", strerror(errno));
      timer_delete(p->timerid); p->timerid = 0;
      return;
  }
}

static void event_handler(wrapper_class_t p)
{
  if (p->is_alarm_fired){
      something_is_on_fire(p);
      start_timer_for_alarm(p);
  }
  else{
      if (p->timerid != 0){
          timer_delete(p->timerid); p->timerid = 0;
      }
      defuse_the_fire_alarm(p);
  }
}

//Do not forget to delete this timer in wrapper class's destroy method!
//for abnormal exit path.
static void wrapper_class_deconstructor(wrapper_class_t p)
{
  if (p->timerid != 0){
      timer_delete(p->timerid); p->timerid = 0;
  }
  free(p);
}

int32_t main(int32_t argc, char** argv)
{
  wrapper_class_t p = (wrapper_class_t)calloc(1, sizeof(wrapper_class));
  if (p == NULL)
      return EXIT_FAILURE;
  
  p->is_alarm_fired = 1;
  event_handler(p);
  sleep(55);
  
  p->is_alarm_fired = 0;
  event_handler(p);
  wrapper_class_deconstructor(p);

  return EXIT_SUCCESS;
}

#ifdef __RUNNING_LOG
shell@android:/data/app # logwrapper ./test_pthread_timer
07-01 03:01:37.248 I/test_pthread_timer( 2945): something_is_on_fire, is_alarm_fired 1
07-01 03:01:37.248 I/test_pthread_timer( 2945): timer ID is 0x80000000
07-01 03:01:47.248 I/test_pthread_timer( 2945): something_is_on_fire, is_alarm_fired 1
07-01 03:01:57.248 I/test_pthread_timer( 2945): something_is_on_fire, is_alarm_fired 1
07-01 03:02:07.248 I/test_pthread_timer( 2945): something_is_on_fire, is_alarm_fired 1
07-01 03:02:17.248 I/test_pthread_timer( 2945): something_is_on_fire, is_alarm_fired 1
07-01 03:02:27.248 I/test_pthread_timer( 2945): something_is_on_fire, is_alarm_fired 1
07-01 03:02:32.248 I/test_pthread_timer( 2945): defuse_the_fire_alarm, is_alarm_fired 0
#endif

3 netlink NETLINK_KOBJECT_UEVENT: same purpose as #1:

(ueventd-example.c) download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
/*
 * Copyright (C) 2013 Yongbing Chen <yongbing.chen.wh@gmail.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <stdlib.h>
#include <errno.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#include <sys/types.h>
#include <unistd.h>

static int32_t init_event_sock(void)
{
    struct sockaddr_nl netlink_addr;
    const int32_t buffersize = 128 * 1024;
    int32_t ret = 0;

    memset(&netlink_addr, 0x00, sizeof(struct sockaddr_nl));
    netlink_addr.nl_family = AF_NETLINK;
    netlink_addr.nl_pid = getpid();
    netlink_addr.nl_groups = 1;

    int32_t event_socket = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
    if (event_socket == -1) {
        int32_t err = errno;
        printf("error getting socket: %s", strerror(errno));
        return -err;
    }

    /* set receive buffersize */
    setsockopt(event_socket, SOL_SOCKET, SO_RCVBUFFORCE, &buffersize, sizeof(buffersize));

    ret = bind(event_socket, (struct sockaddr *) &netlink_addr, sizeof(struct sockaddr_nl));
    if (ret < 0) {
        int32_t err = errno;
        printf("bind failed: %s", strerror(errno));
        close(event_socket);
        return -err;
    }

    return event_socket;
}




int32_t main(int32_t argc, char **argv)
{
  char event_sock_buf[1024] = {0};

  int32_t event_socket = init_event_sock();
  if (event_socket < 0){
      return EXIT_FAILURE;
  }

  while (1){
      int32_t len = TEMP_FAILURE_RETRY(recv(event_socket, &event_sock_buf, sizeof(event_sock_buf), 0));
      if (len < 0){
          printf("recv failed: %s", strerror(errno));
          return EXIT_FAILURE;
      }
      int32_t i = 0;
      while (i < len) {
          printf("next event in buf: %s\n",event_sock_buf+i);
          i += strlen(event_sock_buf+i)+1;
      }
  }
  close(event_socket);
  return EXIT_SUCCESS;
}


#ifdef __RUNNING_LOG
shell@android:/data/app # ./test_uevent
[ 4550.363457] usb 1-1: USB disconnect, device number 2
...
next event in buf: remove@/devices/soc.0/f7ed0000.usb/usb1/1-1/1-1:1.2/0003:046D:C52B.0003/hidraw/hidraw0
next event in buf: ACTION=remove
next event in buf: DEVPATH=/devices/soc.0/f7ed0000.usb/usb1/1-1/1-1:1.2/0003:046D:C52B.0003/hidraw/hidraw0
next event in buf: SUBSYSTEM=hidraw
next event in buf: MAJOR=249
next event in buf: MINOR=0
next event in buf: DEVNAME=hidraw0
next event in buf: SEQNUM=878
...

[ 4554.467274] usb 1-1: new full-speed USB device number 3 using berlin-ehci
[ 4554.626594] usb 1-1: New USB device found, idVendor=046d, idProduct=c52b
[ 4554.633604] usb 1-1: New USB device strings: Mfr=1, Product=2, SerialNumber=0
[ 4554.641231] usb 1-1: Product: USB Receiver
[ 4554.645650] usb 1-1: Manufacturer: Logitech
[ 4554.664080] logitech-djreceiver 0003:046D:C52B.0007: hiddev0,hidraw0: USB HID v1.11 Device [Logitech USB Receiver] on usb-f7ed0000.usb-1/input2
[ 4554.686310] input: Logitech Unifying Device. Wireless PID:2012 as /devices/soc.0/f7ed0000.usb/usb1/1-1/1-1:1.2/0003:046D:C52B.0007/input/input3
...
next event in buf: add@/devices/soc.0/f7ed0000.usb/usb1/1-1/1-1:1.2/0003:046D:C52B.0007/hidraw/hidraw0
next event in buf: ACTION=add
next event in buf: DEVPATH=/devices/soc.0/f7ed0000.usb/usb1/1-1/1-1:1.2/0003:046D:C52B.0007/hidraw/hidraw0
next event in buf: SUBSYSTEM=hidraw
next event in buf: MAJOR=249
next event in buf: MINOR=0
next event in buf: DEVNAME=hidraw0
next event in buf: SEQNUM=896
#endif

Comments