REM (c) Copyright Barry Kauler, June 2013, bkhome.org
REM License GPL3 (refer: /usr/share/doc/legal)
REM libc ref: http://www.gnu.org/software/libc/manual/html_node/Function-Index.html#Function-Index
REM this little daemon reads kernel uevents.
REM vovchik: compile like this, reduces executable 34K to 9K...
REM bacon -o -s -o -Os -o -fdata-sections -o -ffunction-sections -o -Wl,--gc-sections -d /tmp $myfile
REM 130614 forgot to detect sr0/sr1 devices.
REM 130629 support clients in new pup_event IPC mechanism.

REM initialization script... note, RETVAL has exit status.
SYSTEM "/usr/local/pup_event/frontend_startup"
IF RETVAL!=0 THEN END 9

'130629 for reading contents of /tmp/pup_event_ipc...
PROTO opendir, readdir
DECLARE dir1 TYPE DIR*
DECLARE ent1 TYPE struct dirent*

REM from linux/netlink.h...
CONST NETLINK_KOBJECT_UEVENT=15

REM linux/poll.h, only includes asm/poll.h, which only includes asm-generic/poll.h...
CONST POLLIN=1

REM C functions, in libc.so. note, I originally used IMPORT, but PROTO is simpler...
PROTO getpid, socket, bind, poll, recv, printf, strlen, strcmp

REM struct sockaddr_nl, defined in linux/netlink.h...
RECORD nls
 LOCAL nl_family TYPE unsigned short
 LOCAL nl_pad TYPE unsigned short
 LOCAL nl_pid TYPE unsigned int
 LOCAL nl_groups TYPE unsigned int
END RECORD

REM struct pollfd, defined in asm-generic/poll.h...
RECORD pfd
 LOCAL fd TYPE int
 LOCAL events TYPE short
 LOCAL revents TYPE short
END RECORD

DECLARE buf TYPE char ARRAY 512
DECLARE eventstatus TYPE int
DECLARE bufin TYPE char*

DECLARE clientdescr TYPE int

size_char=SIZEOF(char)
size_buf=size_char*512
size_us=SIZEOF(unsigned short)
size_ui=SIZEOF(unsigned int)
size_nls=(2*size_us)+(2*size_ui)

REM initialise the nls structure...
nls.nl_family = AF_NETLINK
nls.nl_pad = 0
nls.nl_pid = getpid()
nls.nl_groups = -1

REM Open hotplug event netlink socket...
pfd.events = POLLIN
pfd.fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT)
IF pfd.fd == -1 THEN END 1

REM listen to netlink socket... 
'USEC
 'int retval=bind(pfd.fd, (void *)&nls.nl_family, size_nls);
 retval=bind(pfd.fd, (void *)&nls.nl_family, size_nls)
'END USEC
IF retval==-1 THEN END 2

cnt=0
devevents$=""
WHILE TRUE DO
 REM 2 second timeout... note, -1 is wait indefinitely.
 eventstatus=poll(&pfd.fd, 1, 1000)
 IF eventstatus==-1 THEN
  END 3
 END IF
 IF eventstatus==0 THEN
  REM one-second timeout.
  REM graceful exit if shutdown X (see /usr/bin/restartwm,wmreboot,wmpoweroff)...
  IF FILEEXISTS("/tmp/wmexitmode.txt")==1 THEN
   END 8
  END IF
  IF devevents$!="" THEN
   exe_change$=CONCAT$("/usr/local/pup_event/frontend_change ",devevents$)
   SYSTEM exe_change$

   '130629 also post block-drive events to any ipc client...
   'look for any files named /tmp/pup_event_ipc/block_* ...
   dir1=opendir("/tmp/pup_event_ipc")
   IF dir1!=NULL THEN
    WHILE TRUE DO
     ent1=readdir(dir1)
     IF ent1==NULL THEN 
      BREAK
     END IF
     off1=INSTR((*ent1).d_name,"block_")
     IF off1!=0 THEN
      clientfile$=CONCAT$("/tmp/pup_event_ipc/",(*ent1).d_name)
      outmsg$=CONCAT$(devevents$,"\n")
      OPTION DEVICE O_WRONLY|O_APPEND
      OPEN clientfile$ FOR DEVICE AS clientdescr
      IF clientdescr>0 THEN
       wr=write(clientdescr,outmsg$,strlen(outmsg$))
       'close(clientdescr)
      END IF
     END IF
    WEND
   END IF
   
   devevents$=""
  ELSE
   REM want to call a pup_event script every four seconds...
   INCR cnt
   IF cnt>=4 THEN
    SYSTEM "/usr/local/pup_event/frontend_timeout"
    cnt=0
   END IF
  END IF
  CONTINUE
 END IF
 
 REM get the uevent...
 len = recv(pfd.fd, buf, size_buf, MSG_DONTWAIT)
 IF len==-1 THEN 
   END 4
 END IF
 REM process the uevent...
 REM only add@, remove@, change@ uevents...
 devevent$=""
 IF buf[0]=='a' THEN
  IF buf[1]=='d' THEN
   IF buf[2]=='d' THEN 
     devevent$="add:"
   END IF
  END IF
 END IF
 IF buf[0]=='r' THEN
  IF buf[1]=='e' THEN
   IF buf[2]=='m' THEN
     devevent$="rem:"
   END IF
  END IF
 END IF
 IF buf[0]=='c' THEN
  IF buf[1]=='h' THEN
   IF buf[2]=='a' THEN
     devevent$="cha:"
   END IF
  END IF
 END IF
 IF devevent$=="" THEN
   CONTINUE
 END IF
 REM want uevents that have "SUBSYSTEM=block"...
 REM buf has a sequence of zero-delimited strings...
 i=0
 flag_block=0
 devname$=""
 WHILE i<len DO
  bufin=buf+i
  IF flag_block==1 THEN
   REM ex: DEVNAME=sdc DEVTYPE=disk  ex2: DEVNAME=sdc1 DEVTYPE=partition
   IF INSTR(bufin,"DEVNAME")!=0 THEN
    devname$=EXTRACT$(bufin,"DEVNAME=")
    REM ignore loop or ram devices... 130614 fix...
    IF REGEX(devname$,"^sd|^hd|^mmc|^sr")==0 THEN
     CONTINUE 2
    END IF
    devevents$=CONCAT$(devevents$,devevent$,devname$," ")
   END IF
  ELSE
   IF strcmp(bufin,"SUBSYSTEM=block")==0 THEN
    flag_block=1
   END IF
  END IF
  i = i+strlen(bufin)+1
 WEND

WEND
