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
|
#ifdef __linux__ /* only build on linux */
#include <ruby.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#include <linux/connector.h>
#define _LINUX_TIME_H
#include <linux/cn_proc.h>
#include <errno.h>
static VALUE mGod;
static VALUE cNetlinkHandler;
static VALUE cEventHandler;
static ID proc_exit;
static ID proc_fork;
static ID m_call;
static ID m_watching_pid;
static int nl_sock; /* socket for netlink connection */
VALUE
nlh_handle_events()
{
char buff[CONNECTOR_MAX_MSG_SIZE];
struct nlmsghdr *hdr;
struct proc_event *event;
VALUE extra_data;
fd_set fds;
FD_ZERO(&fds);
FD_SET(nl_sock, &fds);
if (0 > rb_thread_select(nl_sock + 1, &fds, NULL, NULL, NULL)) {
rb_raise(rb_eStandardError, "%s", strerror(errno));
}
/* If there were no events detected, return */
if (! FD_ISSET(nl_sock, &fds)) {
return INT2FIX(0);
}
/* if there are events, make calls */
if (-1 == recv(nl_sock, buff, sizeof(buff), 0)) {
rb_raise(rb_eStandardError, "%s", strerror(errno));
}
hdr = (struct nlmsghdr *)buff;
if (NLMSG_ERROR == hdr->nlmsg_type) {
rb_raise(rb_eStandardError, "%s", strerror(errno));
} else if (NLMSG_DONE == hdr->nlmsg_type) {
event = (struct proc_event *)((struct cn_msg *)NLMSG_DATA(hdr))->data;
switch(event->what) {
case PROC_EVENT_EXIT:
if (Qnil == rb_funcall(cEventHandler, m_watching_pid, 1, INT2FIX(event->event_data.exit.process_pid))) {
return INT2FIX(0);
}
extra_data = rb_hash_new();
rb_hash_aset(extra_data, ID2SYM(rb_intern("pid")), INT2FIX(event->event_data.exit.process_pid));
rb_hash_aset(extra_data, ID2SYM(rb_intern("exit_code")), INT2FIX(event->event_data.exit.exit_code));
rb_hash_aset(extra_data, ID2SYM(rb_intern("exit_signal")), INT2FIX(event->event_data.exit.exit_signal));
rb_hash_aset(extra_data, ID2SYM(rb_intern("thread_group_id")), INT2FIX(event->event_data.exit.process_tgid));
rb_funcall(cEventHandler, m_call, 3, INT2FIX(event->event_data.exit.process_pid), ID2SYM(proc_exit), extra_data);
return INT2FIX(1);
case PROC_EVENT_FORK:
if (Qnil == rb_funcall(cEventHandler, m_watching_pid, 1, INT2FIX(event->event_data.fork.parent_pid))) {
return INT2FIX(0);
}
extra_data = rb_hash_new();
rb_hash_aset(extra_data, ID2SYM(rb_intern("parent_pid")), INT2FIX(event->event_data.fork.parent_pid));
rb_hash_aset(extra_data, ID2SYM(rb_intern("parent_thread_group_id")), INT2FIX(event->event_data.fork.parent_tgid));
rb_hash_aset(extra_data, ID2SYM(rb_intern("child_pid")), INT2FIX(event->event_data.fork.child_pid));
rb_hash_aset(extra_data, ID2SYM(rb_intern("child_thread_group_id")), INT2FIX(event->event_data.fork.child_tgid));
rb_funcall(cEventHandler, m_call, 3, INT2FIX(event->event_data.fork.parent_pid), ID2SYM(proc_fork), extra_data);
return INT2FIX(1);
default:
break;
}
}
return Qnil;
}
#define NL_MESSAGE_SIZE (sizeof(struct nlmsghdr) + sizeof(struct cn_msg) + \
sizeof(int))
void
connect_to_netlink()
{
struct sockaddr_nl sa_nl; /* netlink interface info */
char buff[NL_MESSAGE_SIZE];
struct nlmsghdr *hdr; /* for telling netlink what we want */
struct cn_msg *msg; /* the actual connector message */
/* connect to netlink socket */
nl_sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR);
if (-1 == nl_sock) {
rb_raise(rb_eStandardError, "%s", strerror(errno));
}
bzero(&sa_nl, sizeof(sa_nl));
sa_nl.nl_family = AF_NETLINK;
sa_nl.nl_groups = CN_IDX_PROC;
sa_nl.nl_pid = getpid();
if (-1 == bind(nl_sock, (struct sockaddr *)&sa_nl, sizeof(sa_nl))) {
rb_raise(rb_eStandardError, "%s", strerror(errno));
}
/* Fill header */
hdr = (struct nlmsghdr *)buff;
hdr->nlmsg_len = NL_MESSAGE_SIZE;
hdr->nlmsg_type = NLMSG_DONE;
hdr->nlmsg_flags = 0;
hdr->nlmsg_seq = 0;
hdr->nlmsg_pid = getpid();
/* Fill message */
msg = (struct cn_msg *)NLMSG_DATA(hdr);
msg->id.idx = CN_IDX_PROC; /* Connecting to process information */
msg->id.val = CN_VAL_PROC;
msg->seq = 0;
msg->ack = 0;
msg->flags = 0;
msg->len = sizeof(int);
*(int*)msg->data = PROC_CN_MCAST_LISTEN;
if (-1 == send(nl_sock, hdr, hdr->nlmsg_len, 0)) {
rb_raise(rb_eStandardError, "%s", strerror(errno));
}
}
void
Init_netlink_handler_ext()
{
proc_exit = rb_intern("proc_exit");
proc_fork = rb_intern("proc_fork");
m_call = rb_intern("call");
m_watching_pid = rb_intern("watching_pid?");
mGod = rb_const_get(rb_cObject, rb_intern("God"));
cEventHandler = rb_const_get(mGod, rb_intern("EventHandler"));
cNetlinkHandler = rb_define_class_under(mGod, "NetlinkHandler", rb_cObject);
rb_define_singleton_method(cNetlinkHandler, "handle_events", nlh_handle_events, 0);
connect_to_netlink();
}
#endif
|