18 Maxhost= 64, /* maximum host name size */
19 Maxservice= 64, /* maximum service name size */
20 Maxactive= 200, /* maximum number of active slave procs */
26 typedef struct Mfile Mfile;
27 typedef struct Mlist Mlist;
28 typedef struct Network Network;
29 typedef struct Flushreq Flushreq;
30 typedef struct Job Job;
32 int vers; /* incremented each clone/attach */
36 int busy; /* fid in use */
37 int ref; /* cleanup when drops to zero */
52 * result of the last lookup
85 jmp_buf masterjmp; /* return through here after a slave process has been created */
86 int *isslave; /* *isslave non-zero means this is a slave process */
87 long active; /* number of active slaves */
94 void rattach(Job*, Mfile*);
95 char* rwalk(Job*, Mfile*);
96 void ropen(Job*, Mfile*);
97 void rcreate(Job*, Mfile*);
98 void rread(Job*, Mfile*);
99 void rwrite(Job*, Mfile*);
100 void rclunk(Job*, Mfile*);
101 void rremove(Job*, Mfile*);
102 void rstat(Job*, Mfile*);
103 void rwstat(Job*, Mfile*);
105 void sendmsg(Job*, char*);
107 void mountinit(char*, char*);
112 char *genquery(Mfile*, char*);
113 char* ipinfoquery(Mfile*, char**, int);
114 int needproto(Network*, Ndbtuple*);
116 Ndbtuple* reorder(Ndbtuple*, Ndbtuple*);
118 void readipinterfaces(void);
120 char* estrdup(char*);
123 void setext(char*, int, char*);
124 void cleanmf(Mfile*);
126 QLock dblock; /* mutex on database operations */
127 QLock netlock; /* mutex for netinit() */
129 char *logfile = "cs";
130 char *paranoiafile = "cs.paranoia";
133 char netndb[Maxpath];
136 * Network specific translators
138 Ndbtuple* iplookup(Network*, char*, char*);
139 char* iptrans(Ndbtuple*, Network*, char*, char*, int);
140 Ndbtuple* telcolookup(Network*, char*, char*);
141 char* telcotrans(Ndbtuple*, Network*, char*, char*, int);
142 Ndbtuple* dnsiplookup(char*, Ndbs*, int);
147 Ndbtuple *(*lookup)(Network*, char*, char*);
148 char *(*trans)(Ndbtuple*, Network*, char*, char*, int);
150 char considered; /* flag: ignored for "net!"? */
151 char fasttimeouthack; /* flag. was for IL */
163 * net doesn't apply to (r)udp, icmp(v6), or telco (for speed).
165 Network network[] = {
166 { "il", iplookup, iptrans, 0, 1, 1, 0, },
167 { "tcp", iplookup, iptrans, 0, 0, 0, 0, },
168 { "il", iplookup, iptrans, 0, 0, 1, 0, },
169 { "udp", iplookup, iptrans, 1, 0, 0, 0, },
170 { "icmp", iplookup, iptrans, 1, 0, 1, 0, },
171 { "icmpv6", iplookup, iptrans, 1, 0, 0, 1, },
172 { "rudp", iplookup, iptrans, 1, 0, 1, 0, },
173 { "ssh", iplookup, iptrans, 1, 0, 0, 0, },
174 { "telco", telcolookup, telcotrans, 1, 0, 0, 0, },
181 char eaddr[16]; /* ascii ethernet address */
182 char ipaddr[64]; /* ascii internet address */
183 uchar ipa[IPaddrlen]; /* binary internet address */
186 Network *netlist; /* networks ordered by preference */
190 nstrcpy(char *to, char *from, int len)
192 strncpy(to, from, len);
199 fprint(2, "usage: %s [-dn] [-f ndb-file] [-x netmtpt]\n", argv0);
204 * based on libthread's threadsetname, but drags in less library code.
205 * actually just sets the arguments displayed.
208 procsetname(char *fmt, ...)
216 cmdname = vsmprint(fmt, arg);
220 snprint(buf, sizeof buf, "#p/%d/args", getpid());
221 if((fd = open(buf, OWRITE)) >= 0){
222 write(fd, cmdname, strlen(cmdname)+1);
229 main(int argc, char *argv[])
232 char ext[Maxpath], servefile[Maxpath];
235 setnetmtpt(mntpt, sizeof(mntpt), nil);
245 dbfile = EARGF(usage());
251 setnetmtpt(mntpt, sizeof(mntpt), EARGF(usage()));
252 setext(ext, sizeof(ext), mntpt);
258 snprint(netndb, sizeof(netndb), "%s/ndb", mntpt);
260 fmtinstall('E', eipfmt);
261 fmtinstall('I', eipfmt);
262 fmtinstall('M', eipfmt);
263 fmtinstall('F', fcallfmt);
269 snprint(servefile, sizeof(servefile), "#s/cs%s", ext);
270 unmount(servefile, mntpt);
273 rfork(RFREND|RFNOTEG);
274 csuser = estrdup(getuser());
275 mountinit(servefile, mntpt);
282 * if a mount point is specified, set the cs extention to be the mount point
283 * with '_'s replacing '/'s
286 setext(char *ext, int n, char *p)
291 for(i = 0; i < n; i++){
303 mountinit(char *service, char *mntpt)
310 error("pipe failed");
315 f = create(service, OWRITE|ORCLOSE, 0666);
318 snprint(buf, sizeof(buf), "%d", p[1]);
319 if(write(f, buf, strlen(buf)) != strlen(buf))
320 error("write /srv/cs");
322 switch(rfork(RFFDG|RFPROC|RFNAMEG)){
325 procsetname("%s", mntpt);
328 error("fork failed");
331 * put ourselves into the file system
334 if(mount(p[1], -1, mntpt, MAFTER, "") < 0)
335 error("mount failed");
338 mfd[0] = mfd[1] = p[0];
344 db = ndbopen(dbfile);
346 error("can't open network database");
348 for(netdb = db; netdb != nil; netdb = netdb->next)
349 if(strcmp(netdb->file, netndb) == 0)
352 netdb = ndbopen(netndb);
355 db = ndbcat(netdb, db);
366 for(f = mlist; f != nil; f = f->next)
367 if(f->mf.busy && f->mf.fid == fid)
369 else if(ff == nil && !f->mf.busy && !f->mf.ref)
372 ff = emalloc(sizeof *f);
377 memset(mf, 0, sizeof *mf);
387 job = emalloc(sizeof *job);
391 job->request.tag = -1;
402 for(l = &joblist; *l != nil; l = &(*l)->next){
418 for(job = joblist; job != nil; job = job->next){
419 if(job->request.tag == tag && job->request.type != Tflush){
433 uchar mdata[IOHDRSZ + Maxfdata];
437 * if we ask dns to fulfill requests,
438 * a slave process is created to wait for replies. The
439 * master process returns immediately via a longjmp
440 * through 'masterjmp'.
442 * *isslave is a pointer into the call stack to a variable
443 * that tells whether or not the current process is a slave.
445 slaveflag = 0; /* init slave variable */
446 isslave = &slaveflag;
450 n = read9pmsg(mfd[0], mdata, sizeof mdata);
456 if(convM2S(mdata, n, &job->request) != n){
457 syslog(1, logfile, "format error %ux %ux %ux %ux %ux",
458 mdata[0], mdata[1], mdata[2], mdata[3], mdata[4]);
463 mf = newfid(job->request.fid);
465 syslog(0, logfile, "%F", &job->request);
467 switch(job->request.type){
469 syslog(1, logfile, "unknown request type %d", job->request.type);
516 * slave processes die after replying
520 syslog(0, logfile, "slave death %d", getpid());
530 if(job->request.msize > IOHDRSZ + Maxfdata)
531 job->reply.msize = IOHDRSZ + Maxfdata;
533 job->reply.msize = job->request.msize;
534 if(strncmp(job->request.version, "9P2000", 6) != 0)
535 sendmsg(job, "unknown 9P version");
537 job->reply.version = "9P2000";
545 sendmsg(job, "cs: authentication not required");
549 * don't flush till all the slaves are done
554 flushjob(job->request.oldtag);
559 rattach(Job *job, Mfile *mf)
563 mf->user = estrdup(job->request.uname);
565 mf->qid.vers = vers++;
566 mf->qid.type = QTDIR;
568 job->reply.qid = mf->qid;
574 rwalk(Job *job, Mfile *mf)
585 elems = job->request.wname;
586 nelems = job->request.nwname;
587 job->reply.nwqid = 0;
589 if(job->request.newfid != job->request.fid){
591 nmf = newfid(job->request.newfid);
594 err = "clone to used channel";
598 nmf->user = estrdup(mf->user);
599 nmf->fid = job->request.newfid;
600 nmf->qid.vers = vers++;
603 /* else nmf will be nil */
608 for(i=0; i<nelems && i<MAXWELEM; i++){
609 if((qid.type & QTDIR) == 0){
610 err = "not a directory";
613 if(strcmp(elems[i], "..") == 0 || strcmp(elems[i], ".") == 0){
617 job->reply.wqid[i] = qid;
621 if(strcmp(elems[i], "cs") == 0){
626 err = "file does not exist";
632 if(nmf != nil && (err!=nil || job->reply.nwqid<nelems)){
646 ropen(Job *job, Mfile *mf)
652 mode = job->request.mode;
653 if(mf->qid.type & QTDIR){
655 err = "permission denied";
657 job->reply.qid = mf->qid;
658 job->reply.iounit = 0;
663 rcreate(Job *job, Mfile *mf)
666 sendmsg(job, "creation permission denied");
670 rread(Job *job, Mfile *mf)
673 long off, toff, clock;
680 off = job->request.offset;
681 cnt = job->request.count;
684 if(mf->qid.type & QTDIR){
687 memset(&dir, 0, sizeof dir);
689 dir.qid.type = QTFILE;
697 dir.atime = clock; /* wrong */
698 dir.mtime = clock; /* wrong */
699 n = convD2M(&dir, buf, sizeof buf);
701 job->reply.data = (char*)buf;
706 /* look for an answer at the right offset */
708 for(i = 0; mf->reply[i] != nil && i < mf->nreply; i++){
715 break; /* got something to return */
717 /* try looking up more answers */
718 if(lookup(mf) == 0 || job->flushed){
725 /* give back a single reply (or part of one) */
726 job->reply.data = mf->reply[i] + (off - toff);
727 if(cnt > toff - off + n)
733 job->reply.count = n;
736 if(--mf->ref == 0 && mf->busy == 0)
761 for(i = 0; i < mf->nreply; i++){
767 mf->nextnet = netlist;
771 rwrite(Job *job, Mfile *mf)
779 cnt = job->request.count;
780 if(mf->qid.type & QTDIR){
781 err = "can't write directory";
784 if(cnt >= Maxrequest){
785 err = "request too long";
788 job->request.data[cnt] = 0;
790 if(strcmp(mf->user, "none") == 0 || strcmp(mf->user, csuser) != 0)
791 goto query; /* skip special commands if not owner */
796 if(strncmp(job->request.data, "debug", 5)==0){
798 syslog(1, logfile, "debug %d", debug);
803 * toggle ipv6 lookups
805 if(strncmp(job->request.data, "ipv6", 4)==0){
807 syslog(1, logfile, "ipv6lookups %d", ipv6lookups);
814 if(strncmp(job->request.data, "paranoia", 8)==0){
816 syslog(1, logfile, "paranoia %d", paranoia);
821 * add networks to the default list
823 if(strncmp(job->request.data, "add ", 4)==0){
824 if(job->request.data[cnt-1] == '\n')
825 job->request.data[cnt-1] = 0;
826 netadd(job->request.data+4);
834 if(strncmp(job->request.data, "refresh", 7)==0){
841 err = "query already in progress";
846 /* start transaction with a clean slate */
850 * look for a general query
852 if(*job->request.data == '!'){
853 err = genquery(mf, job->request.data+1);
858 syslog(0, logfile, "write %s", job->request.data);
860 syslog(0, paranoiafile, "write %s by %s", job->request.data, mf->user);
864 n = getfields(job->request.data, field, 4, 1, "!");
867 mf->net = estrdup("net");
868 mf->host = estrdup(field[0]);
871 mf->rem = estrdup(field[3]);
874 mf->serv = estrdup(field[2]);
877 mf->host = estrdup(field[1]);
878 mf->net = estrdup(field[0]);
883 * do the first net worth of lookup
886 rerrstr(curerr, sizeof curerr);
891 if(--mf->ref == 0 && mf->busy == 0)
895 job->reply.count = cnt;
900 rclunk(Job *job, Mfile *mf)
912 rremove(Job *job, Mfile *mf)
915 sendmsg(job, "remove permission denied");
919 rstat(Job *job, Mfile *mf)
922 uchar buf[IOHDRSZ+Maxfdata];
924 memset(&dir, 0, sizeof dir);
925 if(mf->qid.type & QTDIR){
927 dir.mode = DMDIR|0555;
937 dir.atime = dir.mtime = time(0);
938 job->reply.nstat = convD2M(&dir, buf, sizeof buf);
939 job->reply.stat = buf;
944 rwstat(Job *job, Mfile *mf)
947 sendmsg(job, "wstat permission denied");
951 sendmsg(Job *job, char *err)
954 uchar mdata[IOHDRSZ + Maxfdata];
958 job->reply.type = Rerror;
959 snprint(ename, sizeof(ename), "cs: %s", err);
960 job->reply.ename = ename;
962 job->reply.type = job->request.type+1;
964 job->reply.tag = job->request.tag;
965 n = convS2M(&job->reply, mdata, sizeof mdata);
967 syslog(1, logfile, "sendmsg convS2M of %F returns 0", &job->reply);
971 if(job->flushed == 0)
972 if(write(mfd[1], mdata, n)!=n)
973 error("mount write");
976 syslog(0, logfile, "%F %d", &job->reply, n);
982 syslog(1, logfile, "%s: %r", s);
989 return ipcmp(ip, IPnoaddr) != 0 && ipcmp(ip, v4prefix) != 0;
993 readipinterfaces(void)
995 if(myipaddr(ipa, mntpt) != 0)
996 ipmove(ipa, IPnoaddr);
997 sprint(ipaddr, "%I", ipa);
999 syslog(0, logfile, "ipaddr is %s", ipaddr);
1003 * get the system name
1016 /* use environment, ether addr, or ipaddr to get system name */
1017 if(mysysname == nil){
1019 * environment has priority.
1021 * on the sgi power the default system name
1022 * is the ip address. ignore that.
1025 p = getenv("sysname");
1028 if(strcmp(attr, "ip") != 0)
1029 mysysname = estrdup(p);
1033 * the /net/ndb contains what the network
1034 * figured out from DHCP. use that name if
1037 if(mysysname == nil && netdb != nil){
1039 for(tt = t = ndbparse(netdb); t != nil; t = t->entry){
1040 if(strcmp(t->attr, "sys") == 0){
1041 mysysname = estrdup(t->val);
1048 /* next network database, ip address, and ether address to find a name */
1049 if(mysysname == nil){
1052 free(ndbgetvalue(db, &s, "ip", ipaddr, "sys", &t));
1056 f = open(mntpt, OREAD);
1058 n = dirreadall(f, &d);
1061 for(f = 0; f < n; f++){
1062 if((d[f].mode & DMDIR) == 0 || strncmp(d[f].name, "ether", 5) != 0)
1064 snprint(buf, sizeof buf, "%s/%s", mntpt, d[f].name);
1065 if(myetheraddr(addr, buf) >= 0){
1066 snprint(eaddr, sizeof(eaddr), "%E", addr);
1067 free(ndbgetvalue(db, &s, "ether", eaddr, "sys", &t));
1074 for(tt = t; tt != nil; tt = tt->entry){
1075 if(strcmp(tt->attr, "sys") == 0){
1076 mysysname = estrdup(tt->val);
1083 /* nothing else worked, use the ip address */
1084 if(mysysname == nil && isvalidip(ipa))
1085 mysysname = estrdup(ipaddr);
1088 /* set /dev/sysname if we now know it */
1089 if(mysysname != nil){
1090 f = open("/dev/sysname", OWRITE);
1092 write(f, mysysname, strlen(mysysname));
1100 * Set up a list of default networks by looking for
1104 netinit(int background)
1106 char clone[Maxpath];
1110 switch(rfork(RFPROC|RFNOTEG|RFMEM|RFNOWAIT)){
1119 /* add the mounted networks to the default list */
1120 for(np = network; np->net != nil; np++){
1123 snprint(clone, sizeof(clone), "%s/%s/clone", mntpt, np->net);
1124 if(access(clone, AEXIST) < 0)
1135 /* find out what our ip address is */
1138 /* set the system name if we need to, these days ip is all we have */
1142 syslog(0, logfile, "mysysname %s eaddr %s ipaddr %s ipa %I",
1143 mysysname?mysysname:"???", eaddr, ipaddr, ipa);
1152 * add networks to the standard list
1161 n = getfields(p, field, 12, 1, " ");
1162 for(i = 0; i < n; i++){
1163 for(np = network; np->net != nil; np++){
1164 if(strcmp(field[i], np->net) != 0)
1180 lookforproto(Ndbtuple *t, char *proto)
1182 for(; t != nil; t = t->entry)
1183 if(strcmp(t->attr, "proto") == 0 && strcmp(t->val, proto) == 0)
1189 * lookup a request. the network "net" means we should pick the
1190 * best network to get there.
1198 char reply[Maxreply];
1202 /* open up the standard db files */
1206 error("can't open mf->network database\n");
1209 return 0; /* must have been a genquery */
1212 if(strcmp(mf->net, "net") == 0){
1214 * go through set of default nets
1216 for(np = mf->nextnet; np != nil && rv == 0; np = np->next){
1217 nt = (*np->lookup)(np, mf->host, mf->serv);
1220 hack = np->fasttimeouthack && !lookforproto(nt, np->net);
1221 for(t = nt; mf->nreply < Nreply && t != nil; t = t->entry){
1222 cp = (*np->trans)(t, np, mf->serv, mf->rem, hack);
1224 /* avoid duplicates */
1225 for(i = 0; i < mf->nreply; i++)
1226 if(strcmp(mf->reply[i], cp) == 0)
1228 if(i == mf->nreply){
1229 /* save the reply */
1230 mf->replylen[mf->nreply] = strlen(cp);
1231 mf->reply[mf->nreply++] = cp;
1244 * if not /net, we only get one lookup
1250 * look for a specific network
1252 for(np = network; np->net != nil; np++){
1253 if(np->fasttimeouthack)
1255 if(strcmp(np->net, mf->net) == 0)
1263 nt = (*np->lookup)(np, mf->host, mf->serv);
1264 for(t = nt; mf->nreply < Nreply && t != nil; t = t->entry){
1265 cp = (*np->trans)(t, np, mf->serv, mf->rem, 0);
1267 mf->replylen[mf->nreply] = strlen(cp);
1268 mf->reply[mf->nreply++] = cp;
1276 * not a known network, don't translate host or service
1279 snprint(reply, sizeof(reply), "%s/%s/clone %s!%s",
1280 mntpt, mf->net, mf->host, mf->serv);
1282 snprint(reply, sizeof(reply), "%s/%s/clone %s",
1283 mntpt, mf->net, mf->host);
1284 mf->reply[0] = estrdup(reply);
1285 mf->replylen[0] = strlen(reply);
1292 * translate an ip service name into a port number. If it's a numeric port
1293 * number, look for restricted access.
1295 * the service '*' needs no translation.
1298 ipserv(Network *np, char *name, char *buf, int blen)
1306 /* '*' means any service */
1307 if(strcmp(name, "*") == 0){
1308 nstrcpy(buf, name, blen);
1312 /* see if it's numeric or symbolic */
1313 for(p = name; *p; p++){
1316 else if(isalpha(*p) || *p == '-' || *p == '$')
1324 p = ndbgetvalue(db, &s, np->net, name, "port", &t);
1328 /* look up only for tcp ports < 1024 to get the restricted
1331 if(atoi(name) < 1024 && strcmp(np->net, "tcp") == 0)
1332 p = ndbgetvalue(db, &s, "port", name, "port", &t);
1338 for(nt = t; nt != nil; nt = nt->entry)
1339 if(strcmp(nt->attr, "restricted") == 0)
1343 snprint(buf, blen, "%s%s", p, restr ? "!r" : "");
1350 * lookup an ip attribute
1353 ipattrlookup(Ndb *db, char *ipa, char *attr, char *val, int vlen)
1360 t = ndbipinfo(db, "ip", ipa, alist, 1);
1363 for(nt = t; nt != nil; nt = nt->entry){
1364 if(strcmp(nt->attr, attr) == 0){
1365 nstrcpy(val, nt->val, vlen);
1371 /* we shouldn't get here */
1379 uchar ip[IPaddrlen];
1380 return parseip(ip, s) != -1 && isv4(ip);
1384 * lookup (and translate) an ip destination
1387 iplookup(Network *np, char *host, char *serv)
1389 char *attr, *dnsname;
1392 char ts[Maxservice];
1393 char dollar[Maxhost];
1394 uchar ip[IPaddrlen];
1395 uchar net[IPaddrlen];
1396 uchar tnet[IPaddrlen];
1401 * start with the service since it's the most likely to fail
1402 * and costs the least
1404 werrstr("can't translate address");
1405 if(serv == nil || ipserv(np, serv, ts, sizeof ts) == nil){
1406 werrstr("can't translate service");
1410 /* for dial strings with no host */
1411 if(strcmp(host, "*") == 0)
1412 return ndbnew("ip", "*");
1415 * hack till we go v6 :: = 0.0.0.0
1417 if(strcmp("::", host) == 0)
1418 return ndbnew("ip", "*");
1422 * '$' means the rest of the name is an attribute that we
1423 * need to search for
1426 if(ipattrlookup(db, ipaddr, host+1, dollar, sizeof dollar))
1431 * turn '[ip address]' into just 'ip address'
1437 nstrcpy(dollar, host, sizeof dollar);
1440 if(x = strchr(++host, ']'))
1445 * just accept addresses
1447 attr = ipattr(host);
1448 if(strcmp(attr, "ip") == 0)
1449 return ndbnew("ip", host);
1452 * give the domain name server the first opportunity to
1453 * resolve domain names. if that fails try the database.
1456 werrstr("can't translate address");
1457 if(strcmp(attr, "dom") == 0)
1458 t = dnsiplookup(host, &s, !np->v4only);
1460 free(ndbgetvalue(db, &s, attr, host, "ip", &t));
1462 dnsname = ndbgetvalue(db, &s, attr, host, "dom", nil);
1464 t = dnsiplookup(dnsname, &s, !np->v4only);
1469 t = dnsiplookup(host, &s, !np->v4only);
1474 * reorder the tuple to have the matched line first and
1475 * save that in the request structure.
1477 t = reorder(t, s.t);
1480 * reorder according to our interfaces
1483 for(ifc = ipifcs; ifc != nil; ifc = ifc->next){
1484 for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next){
1485 maskip(lifc->ip, lifc->mask, net);
1486 for(nt = t; nt; nt = nt->entry){
1487 if(strcmp(nt->attr, "ip") != 0)
1489 parseip(ip, nt->val);
1490 maskip(ip, lifc->mask, tnet);
1491 if(memcmp(net, tnet, IPaddrlen) == 0){
1493 qunlock(&ipifclock);
1499 qunlock(&ipifclock);
1505 * translate an ip address
1508 iptrans(Ndbtuple *t, Network *np, char *serv, char *rem, int hack)
1510 char ts[Maxservice];
1511 char reply[Maxreply];
1514 if(strcmp(t->attr, "ip") != 0)
1517 if(serv == nil || ipserv(np, serv, ts, sizeof ts) == nil){
1518 werrstr("can't translate service");
1522 snprint(x, sizeof(x), "!%s", rem);
1527 snprint(reply, sizeof(reply), "%s/%s/clone %s%s",
1528 mntpt, np->net, ts, x);
1530 if(np->v4only && !isv4str(t->val))
1533 if(np->v6only && isv4str(t->val))
1536 snprint(reply, sizeof(reply), "%s/%s/clone %s!%s%s%s",
1537 mntpt, np->net, t->val, ts, x, hack? "!fasttimeout": "");
1540 return estrdup(reply);
1544 * lookup a telephone number
1547 telcolookup(Network *np, char *host, char *serv)
1554 werrstr("can't translate address");
1555 free(ndbgetvalue(db, &s, "sys", host, "telco", &t));
1557 return ndbnew("telco", host);
1559 return reorder(t, s.t);
1563 * translate a telephone address
1566 telcotrans(Ndbtuple *t, Network *np, char *serv, char *rem, int)
1568 char reply[Maxreply];
1571 if(strcmp(t->attr, "telco") != 0)
1575 snprint(x, sizeof(x), "!%s", rem);
1579 snprint(reply, sizeof(reply), "%s/%s/clone %s!%s%s", mntpt, np->net,
1582 snprint(reply, sizeof(reply), "%s/%s/clone %s%s", mntpt, np->net,
1584 return estrdup(reply);
1588 * reorder the tuple to put x's line first in the entry
1591 reorder(Ndbtuple *t, Ndbtuple *x)
1596 /* find start of this entry's line */
1597 for(line = x; line->entry == line->line; line = line->line)
1601 return t; /* already the first line */
1603 /* remove this line and everything after it from the entry */
1604 for(nt = t; nt->entry != line; nt = nt->entry)
1608 /* make that the start of the entry */
1609 for(nt = line; nt->entry != nil; nt = nt->entry)
1616 * create a slave process to handle a request to avoid one request blocking
1617 * another. parent returns to job loop.
1623 return; /* we're already a slave process */
1624 if(ainc(&active) >= Maxactive){
1628 switch(rfork(RFPROC|RFNOTEG|RFMEM|RFNOWAIT)){
1635 syslog(0, logfile, "slave %d", getpid());
1636 procsetname("%s", host);
1639 longjmp(masterjmp, 1);
1645 dnsip6lookup(char *mntpt, char *buf, Ndbtuple *t)
1649 t6 = dnsquery(mntpt, buf, "ipv6"); /* lookup AAAA dns RRs */
1653 /* convert ipv6 attr to ip */
1654 for (tt = t6; tt != nil; tt = tt->entry)
1655 if (strcmp(tt->attr, "ipv6") == 0)
1656 strcpy(tt->attr, "ip");
1661 /* append t6 list to t list */
1662 for (tt = t; tt->entry != nil; tt = tt->entry)
1669 * call the dns process and have it try to translate a name
1672 dnsiplookup(char *host, Ndbs *s, int v6)
1681 werrstr("too much activity");
1685 if(strcmp(ipattr(host), "ip") == 0)
1686 t = dnsquery(mntpt, host, "ptr");
1688 t = dnsquery(mntpt, host, "ip");
1689 /* special case: query ipv6 (AAAA dns RR) too */
1690 if (v6 && ipv6lookups)
1691 t = dnsip6lookup(mntpt, host, t);
1696 rerrstr(buf, sizeof buf);
1697 if(strstr(buf, "exist") != nil)
1698 werrstr("can't translate address: %s", buf);
1699 else if(strstr(buf, "dns failure") != nil)
1700 werrstr("temporary problem: %s", buf);
1708 qmatch(Ndbtuple *t, char **attr, char **val, int n)
1713 for(i = 1; i < n; i++){
1715 for(nt = t; nt != nil; nt = nt->entry)
1716 if(strcmp(attr[i], nt->attr) == 0)
1717 if(strcmp(val[i], "*") == 0
1718 || strcmp(val[i], nt->val) == 0){
1729 qreply(Mfile *mf, Ndbtuple *t)
1735 for(nt = t; mf->nreply < Nreply && nt != nil; nt = nt->entry){
1736 s_append(s, nt->attr);
1738 s_append(s, nt->val);
1740 if(nt->line != nt->entry){
1741 mf->replylen[mf->nreply] = s_len(s);
1742 mf->reply[mf->nreply++] = estrdup(s_to_c(s));
1756 * generic query lookup. The query is of one of the following
1759 * attr1=val1 attr2=val2 attr3=val3 ...
1761 * returns the matching tuple
1763 * ipinfo attr=val attr1 attr2 attr3 ...
1765 * is like ipinfo and returns the attr{1-n}
1766 * associated with the ip address.
1769 genquery(Mfile *mf, char *query)
1773 char *attr[Maxattr];
1778 n = getfields(query, attr, nelem(attr), 1, " ");
1782 if(strcmp(attr[0], "ipinfo") == 0)
1783 return ipinfoquery(mf, attr, n);
1786 for(i = 0; i < n; i++){
1787 p = strchr(attr[i], '=');
1794 /* give dns a chance */
1795 if((strcmp(attr[0], "dom") == 0 || strcmp(attr[0], "ip") == 0) && val[0]){
1796 t = dnsiplookup(val[0], &s, 1);
1798 if(qmatch(t, attr, val, n)){
1807 /* first pair is always the key. It can't be a '*' */
1808 t = ndbsearch(db, &s, attr[0], val[0]);
1810 /* search is the and of all the pairs */
1812 if(qmatch(t, attr, val, n)){
1819 t = ndbsnext(&s, attr[0], val[0]);
1826 * resolve an ip address
1829 ipresolve(char *attr, char *host)
1831 Ndbtuple *t, *nt, **l;
1833 t = iplookup(&network[Ntcp], host, "*");
1834 for(l = &t; *l != nil; ){
1836 if(strcmp(nt->attr, "ip") != 0){
1842 nstrcpy(nt->attr, attr, sizeof(nt->attr));
1849 ipinfoquery(Mfile *mf, char **list, int n)
1852 int resolve[Maxattr];
1853 Ndbtuple *t, *nt, **l;
1862 /* get search attribute=value, or assume ip=myipaddr */
1864 if((val = strchr(attr, '=')) != nil){
1877 * don't let ndbipinfo resolve the addresses, we're
1881 for(i = 0; i < n; i++)
1882 if(*list[i] == '@'){ /* @attr=val ? */
1884 resolve[i] = 1; /* we'll resolve it */
1889 t = ndbipinfo(db, attr, val, list, n);
1894 for(l = &t; *l != nil;){
1897 /* already an address? */
1898 if(strcmp(ipattr(nt->val), "ip") == 0){
1903 /* user wants it resolved? */
1904 for(i = 0; i < n; i++)
1905 if(strcmp(list[i], nt->attr) == 0)
1907 if(i >= n || resolve[i] == 0){
1912 /* resolve address and replace entry */
1913 *l = ipresolve(nt->attr, nt->val);
1923 /* make it all one line */
1924 for(nt = t; nt != nil; nt = nt->entry){
1925 if(nt->entry == nil)
1928 nt->line = nt->entry;
1943 error("out of memory");
1957 error("out of memory");
1958 memmove(p, s, size);