//******************************************************************************
//*this code is protected by the GNU affero GPLv3
//*author:Sylvain BERTRAND <sylvain.bertrand AT gmail dot com>
//* <digital.ragnarok AT gmail dot com>
//******************************************************************************
#include <ulinux/compiler_types.h>
#include <ulinux/sysc.h>
#include <ulinux/types.h>
#include <ulinux/error.h>
#include <ulinux/socket/socket.h>
#include <ulinux/socket/msg.h>
#include <ulinux/socket/netlink.h>
#include <ulinux/epoll.h>
#include <ulinux/utils/mem.h>
#ifndef QUIET
#include <ulinux/file.h>
#include <ulinux/stat.h>
#include <ulinux/mmap.h>
#include <ulinux/signal/signal.h>
#include <stdarg.h>
#include <ulinux/utils/ascii/string/vsprintf.h>
#endif
#include "globals.h"
#include "uevent.h"
static k_i ep_fd;
static k_i s;
void uevents_cleanup(void)
{
k_l r;
do r=sysc(close,1,ep_fd); while(r==-K_EINTR);
if(K_ISERR(r)){
OUT("ERROR(%ld):unable to close epoll fd\n",r);
sysc(exit_group,1,-1);
}
do r=sysc(close,1,s); while(r==-K_EINTR);
if(K_ISERR(r)){
OUT("ERROR(%ld):unable to close netlink socket\n",r);
sysc(exit_group,1,-1);
}
}
void uevents_setup(void)
{
OUTC(PRE "setting up uevent...");
ep_fd=(k_i)sysc(epoll_create1,1,0);
if(K_ISERR(ep_fd)){
OUT("ERROR(%d):unable to create epoll fd\n",ep_fd);
sysc(exit_group,1,-1);
}
//----------------------------------------------------------------------------
//blocking socket
s=(k_i)sysc(socket,3,K_PF_NETLINK,K_SOCK_RAW,
K_NETLINK_KOBJECT_UEVENT);
if(K_ISERR(s)){
OUT("ERROR(%d):unable to create uevent netlink socket\n",s);
sysc(exit_group,1,-1);
}
//----------------------------------------------------------------------------
k_i recv_buf_sz=128*1024;//128k for kernel buffering
k_l r=sysc(setsockopt,5,s,K_SOL_SOCKET,K_SO_RCVBUFFORCE,&recv_buf_sz,
sizeof(recv_buf_sz));
if(K_ISERR(r)){
OUT("ERROR(%ld):unable to force the size of the socket buffer\n",r);
sysc(exit_group,1,-1);
}
//----------------------------------------------------------------------------
//uevent groups-->only one: 1
struct k_sockaddr_nl addr={K_AF_NETLINK,0,0,1};
r=sysc(bind,3,s,&addr,sizeof(addr));
if(K_ISERR(r)){
OUT("ERROR(%ld):unable to bind address to uevent netlink socket\n",r);
sysc(exit_group,1,-1);
}
//----------------------------------------------------------------------------
struct k_epoll_event ep_evt;
u_memset(&ep_evt,0,sizeof(ep_evt));
ep_evt.events=K_EPOLLIN;
ep_evt.data.fd=s;
r=sysc(epoll_ctl,4,ep_fd,K_EPOLL_CTL_ADD,s,&ep_evt);
if(K_ISERR(r)){
OUT("ERROR(%ld):unable to register uevent netlink socket to epoll\n",r);
sysc(exit_group,1,-1);
}
OUTC("done\n");
}
static void uevent_msg(void)
{
k_u8 buf[8192];//presume 8kB is enough for one message
u_memset(buf,0,sizeof(buf));
struct k_io_vec io_vec;
io_vec.base=buf;
io_vec.len=sizeof(buf);
struct k_msg_hdr msg;
u_memset(&msg,0,sizeof(msg));
msg.iov=&io_vec;
msg.iov_len=1;
k_l r;
do r=sysc(recvmsg,3,s,&msg,0); while(r==-K_EINTR);
if(K_ISERR(r)){
OUT("ERROR(%ld):unable to receive the uevent\n",r);
sysc(exit_group,1,-1);
}
if(msg.flgs&K_MSG_TRUNC){
OUT("ERROR:the uevent was truncated(flags=0x%x)\n",msg.flgs);
sysc(exit_group,1,-1);
}
uevent_process(&buf[0],(k_i)r);
}
void uevents_process(void)
{
OUTC(PRE "processing uevents...\n");
while(1){
k_l r;
static struct k_epoll_event evts;//uevent netlink event
do{
u_memset(&evts,0,sizeof(evts));
r=sysc(epoll_wait,4,ep_fd,&evts,1,UEVENTS_TIMEOUT);
}while(r==-K_EINTR);
if(K_ISERR(r)){
OUT(PRE "ERROR(%ld):error epolling uevent netlink socket\n",r);
sysc(exit_group,1,-1);
}
if(!r) break;//assume no more uevents
if(evts.events&K_EPOLLIN) uevent_msg();
else{
OUT(PRE "ERROR:unmanaged epolling event on uevent netlink socket"
"(events=%u)\n",evts.events);
sysc(exit_group,1,-1);
}
}
OUTC(PRE "uevents processed\n");
}