tdb.c - plan9port - [fork] Plan 9 from user space | |
git clone git://src.adamsgaard.dk/plan9port | |
Log | |
Files | |
Refs | |
README | |
LICENSE | |
--- | |
tdb.c (7590B) | |
--- | |
1 #include <u.h> | |
2 #include <libc.h> | |
3 #include <ip.h> | |
4 #include <bio.h> | |
5 #include <ndb.h> | |
6 #include <ctype.h> | |
7 #include "dat.h" | |
8 | |
9 /* | |
10 * format of a binding entry: | |
11 * char ipaddr[32]; | |
12 * char id[32]; | |
13 * char hwa[32]; | |
14 * char otime[10]; | |
15 */ | |
16 Binding *bcache; | |
17 uchar bfirst[IPaddrlen]; | |
18 char *binddir = nil; | |
19 char *xbinddir = "#9/ndb/dhcp"; | |
20 | |
21 /* | |
22 * convert a byte array to hex | |
23 */ | |
24 static char | |
25 hex(int x) | |
26 { | |
27 if(x < 10) | |
28 return x + '0'; | |
29 return x - 10 + 'a'; | |
30 } | |
31 extern char* | |
32 tohex(char *hdr, uchar *p, int len) | |
33 { | |
34 char *s, *sp; | |
35 int hlen; | |
36 | |
37 hlen = strlen(hdr); | |
38 s = malloc(hlen + 2*len + 1); | |
39 sp = s; | |
40 strcpy(sp, hdr); | |
41 sp += hlen; | |
42 for(; len > 0; len--){ | |
43 *sp++ = hex(*p>>4); | |
44 *sp++ = hex(*p & 0xf); | |
45 p++; | |
46 } | |
47 *sp = 0; | |
48 return s; | |
49 } | |
50 | |
51 /* | |
52 * convert a client id to a string. If it's already | |
53 * ascii, leave it be. Otherwise, convert it to hex. | |
54 */ | |
55 extern char* | |
56 toid(uchar *p, int n) | |
57 { | |
58 int i; | |
59 char *s; | |
60 | |
61 for(i = 0; i < n; i++) | |
62 if(!isprint(p[i])) | |
63 return tohex("id", p, n); | |
64 s = malloc(n + 1); | |
65 memmove(s, p, n); | |
66 s[n] = 0; | |
67 return s; | |
68 } | |
69 | |
70 /* | |
71 * increment an ip address | |
72 */ | |
73 static void | |
74 incip(uchar *ip) | |
75 { | |
76 int i, x; | |
77 | |
78 for(i = IPaddrlen-1; i >= 0; i--){ | |
79 x = ip[i]; | |
80 x++; | |
81 ip[i] = x; | |
82 if((x & 0x100) == 0) | |
83 break; | |
84 } | |
85 } | |
86 | |
87 /* | |
88 * find a binding for an id or hardware address | |
89 */ | |
90 static int | |
91 lockopen(char *file) | |
92 { | |
93 char err[ERRMAX]; | |
94 int fd, tries; | |
95 | |
96 for(tries = 0; tries < 5; tries++){ | |
97 fd = open(file, OLOCK|ORDWR); | |
98 if(fd >= 0) | |
99 return fd; | |
100 errstr(err, sizeof err); | |
101 if(strstr(err, "lock")){ | |
102 /* wait for other process to let go of lock */ | |
103 sleep(250); | |
104 | |
105 /* try again */ | |
106 continue; | |
107 } | |
108 if(strstr(err, "exist") || strstr(err, "No such")){ | |
109 /* no file, create an exclusive access file */ | |
110 fd = create(file, ORDWR, DMEXCL|0666); | |
111 chmod(file, 0666); | |
112 if(fd >= 0) | |
113 return fd; | |
114 } | |
115 } | |
116 return -1; | |
117 } | |
118 | |
119 void | |
120 setbinding(Binding *b, char *id, long t) | |
121 { | |
122 if(b->boundto) | |
123 free(b->boundto); | |
124 | |
125 b->boundto = strdup(id); | |
126 b->lease = t; | |
127 } | |
128 | |
129 static void | |
130 parsebinding(Binding *b, char *buf) | |
131 { | |
132 long t; | |
133 char *id, *p; | |
134 | |
135 /* parse */ | |
136 t = atoi(buf); | |
137 id = strchr(buf, '\n'); | |
138 if(id){ | |
139 *id++ = 0; | |
140 p = strchr(id, '\n'); | |
141 if(p) | |
142 *p = 0; | |
143 } else | |
144 id = ""; | |
145 | |
146 /* replace any past info */ | |
147 setbinding(b, id, t); | |
148 } | |
149 | |
150 static int | |
151 writebinding(int fd, Binding *b) | |
152 { | |
153 Dir *d; | |
154 | |
155 seek(fd, 0, 0); | |
156 if(fprint(fd, "%ld\n%s\n", b->lease, b->boundto) < 0) | |
157 return -1; | |
158 d = dirfstat(fd); | |
159 if(d == nil) | |
160 return -1; | |
161 b->q.type = d->qid.type; | |
162 b->q.path = d->qid.path; | |
163 b->q.vers = d->qid.vers; | |
164 free(d); | |
165 return 0; | |
166 } | |
167 | |
168 /* | |
169 * synchronize cached binding with file. the file always wins. | |
170 */ | |
171 int | |
172 syncbinding(Binding *b, int returnfd) | |
173 { | |
174 char buf[512]; | |
175 int i, fd; | |
176 Dir *d; | |
177 | |
178 if(binddir == nil) | |
179 binddir = unsharp(xbinddir); | |
180 | |
181 snprint(buf, sizeof(buf), "%s/%I", binddir, b->ip); | |
182 fd = lockopen(buf); | |
183 if(fd < 0){ | |
184 /* assume someone else is using it */ | |
185 b->lease = time(0) + OfferTimeout; | |
186 return -1; | |
187 } | |
188 | |
189 /* reread if changed */ | |
190 d = dirfstat(fd); | |
191 if(d != nil) /* BUG? */ | |
192 if(d->qid.type != b->q.type || d->qid.path != b->q.path || d->qi… | |
193 i = read(fd, buf, sizeof(buf)-1); | |
194 if(i < 0) | |
195 i = 0; | |
196 buf[i] = 0; | |
197 parsebinding(b, buf); | |
198 b->lasttouched = d->mtime; | |
199 b->q.path = d->qid.path; | |
200 b->q.vers = d->qid.vers; | |
201 } | |
202 | |
203 free(d); | |
204 | |
205 if(returnfd) | |
206 return fd; | |
207 | |
208 close(fd); | |
209 return 0; | |
210 } | |
211 | |
212 extern int | |
213 samenet(uchar *ip, Info *iip) | |
214 { | |
215 uchar x[IPaddrlen]; | |
216 | |
217 maskip(iip->ipmask, ip, x); | |
218 return ipcmp(x, iip->ipnet) == 0; | |
219 } | |
220 | |
221 /* | |
222 * create a record for each binding | |
223 */ | |
224 extern void | |
225 initbinding(uchar *first, int n) | |
226 { | |
227 while(n-- > 0){ | |
228 iptobinding(first, 1); | |
229 incip(first); | |
230 } | |
231 } | |
232 | |
233 /* | |
234 * find a binding for a specific ip address | |
235 */ | |
236 extern Binding* | |
237 iptobinding(uchar *ip, int mk) | |
238 { | |
239 Binding *b; | |
240 | |
241 for(b = bcache; b; b = b->next){ | |
242 if(ipcmp(b->ip, ip) == 0){ | |
243 syncbinding(b, 0); | |
244 return b; | |
245 } | |
246 } | |
247 | |
248 if(mk == 0) | |
249 return 0; | |
250 b = malloc(sizeof(*b)); | |
251 memset(b, 0, sizeof(*b)); | |
252 ipmove(b->ip, ip); | |
253 b->next = bcache; | |
254 bcache = b; | |
255 syncbinding(b, 0); | |
256 return b; | |
257 } | |
258 | |
259 static void | |
260 lognolease(Binding *b) | |
261 { | |
262 /* renew the old binding, and hope it eventually goes away */ | |
263 b->offer = 5*60; | |
264 commitbinding(b); | |
265 | |
266 /* complain if we haven't in the last 5 minutes */ | |
267 if(now - b->lastcomplained < 5*60) | |
268 return; | |
269 syslog(0, blog, "dhcp: lease for %I to %s ended at %ld but still… | |
270 b->ip, b->boundto != nil ? b->boundto : "?", b->lease); | |
271 b->lastcomplained = now; | |
272 } | |
273 | |
274 /* | |
275 * find a free binding for a hw addr or id on the same network as iip | |
276 */ | |
277 extern Binding* | |
278 idtobinding(char *id, Info *iip, int ping) | |
279 { | |
280 Binding *b, *oldest; | |
281 int oldesttime; | |
282 | |
283 /* | |
284 * first look for an old binding that matches. that way | |
285 * clients will tend to keep the same ip addresses. | |
286 */ | |
287 for(b = bcache; b; b = b->next){ | |
288 if(b->boundto && strcmp(b->boundto, id) == 0){ | |
289 if(!samenet(b->ip, iip)) | |
290 continue; | |
291 | |
292 /* check with the other servers */ | |
293 syncbinding(b, 0); | |
294 if(strcmp(b->boundto, id) == 0) | |
295 return b; | |
296 } | |
297 } | |
298 | |
299 /* | |
300 * look for oldest binding that we think is unused | |
301 */ | |
302 for(;;){ | |
303 oldest = nil; | |
304 oldesttime = 0; | |
305 for(b = bcache; b; b = b->next){ | |
306 if(b->tried != now) | |
307 if(b->lease < now && b->expoffer < now && samene… | |
308 if(oldest == nil || b->lasttouched < oldesttime){ | |
309 /* sync and check again */ | |
310 syncbinding(b, 0); | |
311 if(b->lease < now && b->expoffer < now &… | |
312 if(oldest == nil || b->lasttouched < old… | |
313 oldest = b; | |
314 oldesttime = b->lasttouched; | |
315 } | |
316 } | |
317 } | |
318 if(oldest == nil) | |
319 break; | |
320 | |
321 /* make sure noone is still using it */ | |
322 oldest->tried = now; | |
323 if(ping == 0 || icmpecho(oldest->ip) == 0) | |
324 return oldest; | |
325 | |
326 lognolease(oldest); /* sets lastcomplained */ | |
327 } | |
328 | |
329 /* try all bindings */ | |
330 for(b = bcache; b; b = b->next){ | |
331 syncbinding(b, 0); | |
332 if(b->tried != now) | |
333 if(b->lease < now && b->expoffer < now && samenet(b->ip,… | |
334 b->tried = now; | |
335 if(ping == 0 || icmpecho(b->ip) == 0) | |
336 return b; | |
337 | |
338 lognolease(b); | |
339 } | |
340 } | |
341 | |
342 /* nothing worked, give up */ | |
343 return 0; | |
344 } | |
345 | |
346 /* | |
347 * create an offer | |
348 */ | |
349 extern void | |
350 mkoffer(Binding *b, char *id, long leasetime) | |
351 { | |
352 if(leasetime <= 0){ | |
353 if(b->lease > now + minlease) | |
354 leasetime = b->lease - now; | |
355 else | |
356 leasetime = minlease; | |
357 } | |
358 if(b->offeredto) | |
359 free(b->offeredto); | |
360 b->offeredto = strdup(id); | |
361 b->offer = leasetime; | |
362 b->expoffer = now + OfferTimeout; | |
363 } | |
364 | |
365 /* | |
366 * find an offer for this id | |
367 */ | |
368 extern Binding* | |
369 idtooffer(char *id, Info *iip) | |
370 { | |
371 Binding *b; | |
372 | |
373 /* look for an offer to this id */ | |
374 for(b = bcache; b; b = b->next){ | |
375 if(b->offeredto && strcmp(b->offeredto, id) == 0 && same… | |
376 /* make sure some other system hasn't stolen it … | |
377 syncbinding(b, 0); | |
378 if(b->lease < now | |
379 || (b->boundto && strcmp(b->boundto, b->offeredt… | |
380 return b; | |
381 } | |
382 } | |
383 return 0; | |
384 } | |
385 | |
386 /* | |
387 * commit a lease, this could fail | |
388 */ | |
389 extern int | |
390 commitbinding(Binding *b) | |
391 { | |
392 int fd; | |
393 long now; | |
394 | |
395 now = time(0); | |
396 | |
397 if(b->offeredto == 0) | |
398 return -1; | |
399 fd = syncbinding(b, 1); | |
400 if(fd < 0) | |
401 return -1; | |
402 if(b->lease > now && b->boundto && strcmp(b->boundto, b->offered… | |
403 close(fd); | |
404 return -1; | |
405 } | |
406 setbinding(b, b->offeredto, now + b->offer); | |
407 b->lasttouched = now; | |
408 | |
409 if(writebinding(fd, b) < 0){ | |
410 close(fd); | |
411 return -1; | |
412 } | |
413 close(fd); | |
414 return 0; | |
415 } | |
416 | |
417 /* | |
418 * commit a lease, this could fail | |
419 */ | |
420 extern int | |
421 releasebinding(Binding *b, char *id) | |
422 { | |
423 int fd; | |
424 long now; | |
425 | |
426 now = time(0); | |
427 | |
428 fd = syncbinding(b, 1); | |
429 if(fd < 0) | |
430 return -1; | |
431 if(b->lease > now && b->boundto && strcmp(b->boundto, id) != 0){ | |
432 close(fd); | |
433 return -1; | |
434 } | |
435 b->lease = 0; | |
436 b->expoffer = 0; | |
437 | |
438 if(writebinding(fd, b) < 0){ | |
439 close(fd); | |
440 return -1; | |
441 } | |
442 close(fd); | |
443 return 0; | |
444 } |