Parlib and other user libs are built with -fPIC
[akaros.git] / user / ndblib / ndbipinfo.c
1 /* 
2  * This file is part of the UCB release of Plan 9. It is subject to the license
3  * terms in the LICENSE file found in the top-level directory of this
4  * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
5  * part of the UCB release of Plan 9, including this file, may be copied,
6  * modified, propagated, or distributed except according to the terms contained
7  * in the LICENSE file.
8  */
9 #include <stdlib.h>
10
11 #include <stdio.h>
12 #include <parlib.h>
13 #include <unistd.h>
14 #include <signal.h>
15 #include <fcntl.h>
16 #include <iplib.h>
17 #include <ndb.h>
18
19 enum
20 {
21         Ffound= 1<<0,
22         Fignore=1<<1,
23         Faddr=  1<<2,
24 };
25
26 static struct ndbtuple* filter(struct ndb *db, struct ndbtuple *t,
27                                       struct ndbtuple *f);
28 static struct ndbtuple* mkfilter(int argc, char **argv);
29 static int              filtercomplete(struct ndbtuple *f);
30 static struct ndbtuple* toipaddr(struct ndb *db, struct ndbtuple *t);
31 static int              prefixlen(uint8_t *ip);
32 static struct ndbtuple* subnet(struct ndb *db, uint8_t *net,
33                                       struct ndbtuple *f, int prefix);
34
35 /* make a filter to be used in filter */
36 static struct ndbtuple*
37 mkfilter(int argc, char **argv)
38 {
39         struct ndbtuple *t, *first, *last;
40         char *p;
41
42         last = first = NULL;
43         while(argc-- > 0){
44                 t = ndbnew(0, 0);
45                 if(first)
46                         last->entry = t;
47                 else
48                         first = t;
49                 last = t;
50                 p = *argv++;
51                 if(*p == '@'){                  /* @attr=val ? */
52                         t->ptr |= Faddr;        /* return resolved address(es) */
53                         p++;
54                 }
55                 strncpy(t->attr, p, sizeof(t->attr)-1);
56         }
57         ndbsetmalloctag(first, getcallerpc(&argc));
58         return first;
59 }
60
61 /* return true if every pair of filter has been used */
62 static int
63 filtercomplete(struct ndbtuple *f)
64 {
65         for(; f; f = f->entry)
66                 if((f->ptr & Fignore) == 0)
67                         return 0;
68         return 1;
69 }
70
71 /* set the attribute of all entries in a tuple */
72 static struct ndbtuple*
73 setattr(struct ndbtuple *t, char *attr)
74 {
75         struct ndbtuple *nt;
76
77         for(nt = t; nt; nt = nt->entry)
78                 strcpy(nt->attr, attr);
79         return t;
80 }
81
82 /*
83  *  return only the attr/value pairs in t maching the filter, f.
84  *  others are freed.  line structure is preserved.
85  */
86 static struct ndbtuple*
87 filter(struct ndb *db, struct ndbtuple *t, struct ndbtuple *f)
88 {
89         struct ndbtuple *nt, *nf, *next;
90
91         /* filter out what we don't want */
92         for(nt = t; nt; nt = next){
93                 next = nt->entry;
94
95                 /* look through filter */
96                 for(nf = f; nf != NULL; nf = nf->entry){
97                         if(!(nf->ptr&Fignore) && strcmp(nt->attr, nf->attr) == 0)
98                                 break;
99                 }
100                 if(nf == NULL){
101                         /* remove nt from t */
102                         t = ndbdiscard(t, nt);
103                 } else {
104                         if(nf->ptr & Faddr)
105                                 t = ndbsubstitute(t, nt, setattr(ndbgetipaddr(db, nt->val), nt->attr));
106                         nf->ptr |= Ffound;
107                 }
108         }
109
110         /* remember filter etnries that matched */
111         for(nf = f; nf != NULL; nf = nf->entry)
112                 if(nf->ptr & Ffound)
113                         nf->ptr = (nf->ptr & ~Ffound) | Fignore;
114
115         ndbsetmalloctag(t, getcallerpc(&db));
116         return t;
117 }
118
119 static int
120 prefixlen(uint8_t *ip)
121 {
122         int y, i;
123
124         for(y = IPaddrlen-1; y >= 0; y--)
125                 for(i = 8; i > 0; i--)
126                         if(ip[y] & (1<<(8-i)))
127                                 return y*8 + i;
128         return 0;
129 }
130
131 /*
132  *  look through a containing subset
133  */
134 static struct ndbtuple*
135 subnet(struct ndb *db, uint8_t *net, struct ndbtuple *f, int prefix)
136 {
137         struct ndbs s;
138         struct ndbtuple *t, *nt, *xt;
139         char netstr[128];
140         uint8_t mask[IPaddrlen];
141         int masklen;
142
143         t = NULL;
144         sprintf(netstr, "%I", net);
145         nt = ndbsearch(db, &s, "ip", netstr);
146         while(nt != NULL){
147                 xt = ndbfindattr(nt, nt, "ipnet");
148                 if(xt){
149                         xt = ndbfindattr(nt, nt, "ipmask");
150                         if(xt)
151                                 parseipmask(mask, xt->val);
152                         else
153                                 ipmove(mask, defmask(net));
154                         masklen = prefixlen(mask);
155                         if(masklen <= prefix){
156                                 t = ndbconcatenate(t, filter(db, nt, f));
157                                 nt = NULL;
158                         }
159                 }
160                 ndbfree(nt);
161                 nt = ndbsnext(&s, "ip", netstr);
162         }
163         ndbsetmalloctag(t, getcallerpc(&db));
164         return t;
165 }
166
167 /*
168  *  fill in all the requested attributes for a system.
169  *  if the system's entry doesn't have all required,
170  *  walk through successively more inclusive networks
171  *  for inherited attributes.
172  */
173 struct ndbtuple*
174 ndbipinfo(struct ndb *db, char *attr, char *val, char **alist, int n)
175 {
176         struct ndbtuple *t, *nt, *f;
177         struct ndbs s;
178         char *ipstr;
179         uint8_t net[IPaddrlen], ip[IPaddrlen];
180         int prefix, smallestprefix, force;
181         int64_t r;
182
183 #if 0
184         /* just in case */
185         fmtinstall('I', eipfmt);
186         fmtinstall('M', eipfmt);
187 #endif
188
189         /* get needed attributes */
190         f = mkfilter(n, alist);
191
192         /*
193          *  first look for a matching entry with an ip address
194          */
195         t = NULL;
196         ipstr = ndbgetvalue(db, &s, attr, val, "ip", &nt);
197         if(ipstr == NULL){
198                 /* none found, make one up */
199                 if(strcmp(attr, "ip") != 0) {
200                         ndbfree(f);
201                         return NULL;    
202                 }
203                 t = ndbnew("ip", val);
204                 t->line = t;
205                 t->entry = NULL;
206                 r = parseip(net, val);
207                 if(r == -1)
208                         ndbfree(t);
209         } else {
210                 /* found one */
211                 while(nt != NULL){
212                         nt = ndbreorder(nt, s.t);
213                         t = ndbconcatenate(t, nt);
214                         nt = ndbsnext(&s, attr, val);
215                 }
216                 r = parseip(net, ipstr);
217                 free(ipstr);
218         }
219         if(r < 0){
220                 ndbfree(f);
221                 return NULL;
222         }
223         ipmove(ip, net);
224         t = filter(db, t, f);
225
226         /*
227          *  now go through subnets to fill in any missing attributes
228          */
229         if(isv4(net)){
230                 prefix = 127;
231                 smallestprefix = 100;
232                 force = 0;
233         } else {
234                 /* in v6, the last 8 bytes have no structure (we hope) */
235                 prefix = 64;
236                 smallestprefix = 2;
237                 memset(net+8, 0, 8);
238                 force = 1;
239         }
240
241         /*
242          *  to find a containing network, keep turning off
243          *  the lower bit and look for a network with
244          *  that address and a shorter mask.  tedius but
245          *  complete, we may need to find a trick to speed this up.
246          */
247         for(; prefix >= smallestprefix; prefix--){
248                 if(filtercomplete(f))
249                         break;
250                 if(!force && (net[prefix/8] & (1<<(7-(prefix%8)))) == 0)
251                         continue;
252                 force = 0;
253                 net[prefix/8] &= ~(1<<(7-(prefix%8)));
254                 t = ndbconcatenate(t, subnet(db, net, f, prefix));
255         }
256
257         /*
258          *  if there's an unfulfilled ipmask, make one up
259          */
260         nt = ndbfindattr(f, f, "ipmask");
261         if(nt && !(nt->ptr & Fignore)){
262                 char x[64];
263
264                 snprintf(x, sizeof(x), "%M", defmask(ip));
265                 t = ndbconcatenate(t, ndbnew("ipmask", x));
266         }
267
268         ndbfree(f);
269         ndbsetmalloctag(t, getcallerpc(&db));
270         return t;
271 }