unix/minit

waitport.c in master
Repositories | Summary | Log | Files | README | COPYING

waitport.c (3839B) download


  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
 15static 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') {
 43parseerror:
 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
109int 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)
120usage:
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}