minit

A small yet feature-complete init (http://fefe.de/minit/)
Log | Files | Refs | README | LICENSE

waitport.c (3839B)


      1 #include <unistd.h>
      2 #include <errmsg.h>
      3 #include <time.h>
      4 #include <string.h>
      5 #include <fcntl.h>
      6 #include <ctype.h>
      7 
      8 #include <libowfat/fmt.h>
      9 #include <libowfat/scan.h>
     10 #include <libowfat/ip4.h>
     11 #include <libowfat/ip6.h>
     12 #include <libowfat/stralloc.h>
     13 #include <libowfat/buffer.h>
     14 
     15 static int netstat(const char* addr,unsigned int wantedport) {
     16   /* see linux/Documentation/networking/proc_net_tcp.txt */
     17   int fd=-1;
     18   char rbuf[4096];	/* since these files can become arbitrarily large, we'll use a real buffer to read from them */
     19   const char* filenames[] = { "/proc/net/tcp6", "/proc/net/tcp" };
     20   buffer b;
     21   unsigned int fn;
     22   stralloc line;
     23 
     24   for (fn=0; fn<sizeof(filenames)/sizeof(filenames[0]); ++fn) {
     25     const char* filename=filenames[fn];
     26 
     27     fd=open(filename,O_RDONLY|O_CLOEXEC);
     28     if (fd==-1)
     29       continue;
     30     buffer_init(&b,read,fd,rbuf,sizeof(rbuf));	/* can't fail */
     31     for (;;) {
     32       int r;
     33       char* c;
     34       char* local;
     35       int v6;
     36       stralloc_zero(&line);
     37       if ((r=buffer_getline_sa(&b,&line))==-1) {
     38 	close(fd);
     39 	die(1,"read error from ",filename);
     40       }
     41       if (r==0) break;
     42       if (line.len < 1 || line.s[line.len-1]!='\n') {
     43 parseerror:
     44 	close(fd);
     45 	die(1,"parse error in ",filename);
     46       }
     47       line.s[line.len-1]=0;	/* string is now null terminated */
     48 
     49       /* First token is something like "917:", skip */
     50       for (c=line.s; *c && *c!=':'; ++c) ;
     51       if (*c != ':') continue;	/* first line is boilerplate text and has no :-token, skip it */
     52       ++c;
     53       for (; *c==' '; ++c) ;
     54       /* Next token is something like "00000000:1770" or "00000000000000000000000000000000:0016" */
     55       local=c;
     56       for (; isxdigit(*c); ++c) ;
     57       if (c-local != 8 && c-local != 32)	/* we only support ipv4 and ipv6; this is neither */
     58 	continue;
     59       v6=(c-local==32);
     60       if (*c!=':') goto parseerror;
     61       for (r=1; r<5; ++r) {
     62 	if (!isxdigit(c[r])) goto parseerror;
     63       }
     64       if (c[5]!=' ') goto parseerror;
     65       c[5]=0;
     66       c+=6;
     67       /* Next token is the same thing, but we don't really need it, so
     68        * just skip till next whitespace */
     69       for (; *c && *c!=' '; ++c) ;
     70       if (*c!=' ') goto parseerror;
     71       ++c;
     72       /* Next is the state; if we are looking at tcp, 0A means LISTEN */
     73       if (filename[10]=='t' && c[0]=='0' && c[1]=='A') {
     74 	/* TCP LISTEN */
     75 	size_t n;
     76 	union {
     77 	  char ip[16];
     78 	  uint32_t ints[4];
     79 	} omgwtfbbq;
     80 	char ipstring[FMT_IP6];
     81 	unsigned short port;
     82 	unsigned long temp;
     83 
     84 	/* we can only be here if the hex string is 8 or 32 bytes long, see above */
     85 	for (n=0; n<(unsigned int)v6*3+1; ++n) {
     86 	  scan_xlongn(local+n*8,8,&temp);
     87 	  omgwtfbbq.ints[n]=temp;
     88 	}
     89 
     90 	if (scan_xshort(local+(((unsigned int)v6*3+1)*8)+1,&port)!=4)	/* can't happen, we validated with isxdigit */
     91 	  goto parseerror;
     92 
     93 	ipstring[v6 ? fmt_ip6c(ipstring,omgwtfbbq.ip) : fmt_ip4(ipstring,omgwtfbbq.ip)]=0;
     94 
     95 	if (!strcmp(ipstring,addr?addr : (v6?"::":"0.0.0.0")) && port==wantedport) {
     96 	  close(fd);
     97 	  return 1;
     98 	}
     99 
    100       }
    101     }
    102     close(fd);
    103     fd=-1;
    104   }
    105 
    106   return 0;
    107 }
    108 
    109 int main(int argc,char* argv[],char* envp[]) {
    110   unsigned short port;
    111 
    112   unsigned int i;
    113   struct timespec req,rem;
    114 
    115   char* s=argv[1];
    116   char* t=strchr(s,'/');
    117 
    118   errmsg_iam("waitport");
    119   if (argc<2)
    120 usage:
    121     die(0,"usage: waitport ::/111 some.rpcd\n\twaits for a service to bind to TCP port 111 on ::, then executes the rest of the command line");
    122 
    123   {
    124     if (t) {
    125       *t=0;
    126       if (scan_ushort(t+1,&port)==0) goto usage;
    127     } else {
    128       if (scan_ushort(s,&port)==0) goto usage;
    129       s=0;
    130     }
    131   }
    132 
    133   req.tv_sec=0; req.tv_nsec=100000000;
    134   for (i=0; i<1000; ++i) {
    135     if (netstat(s,port)) {
    136       if (argv[2]) {
    137 	execve(argv[2],argv+2,envp);
    138 	diesys(1,"execve");
    139       }
    140       return 0;
    141     }
    142     nanosleep(&req,&rem);
    143   }
    144   die(1,"service on port not showing up");
    145 }