Libinfo-324.1.tar.gz
[apple/libinfo.git] / lookup.subproj / cache_module.c
1 /*
2 * Copyright (c) 2008-2009 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 #include <si_module.h>
25 #include <stdio.h>
26 #include <unistd.h>
27 #include <string.h>
28 #include <time.h>
29 #include <errno.h>
30 #include <arpa/inet.h>
31 #include <sys/stat.h>
32 #include <ils.h>
33 #include <libkern/OSAtomic.h>
34
35 /* GLOBAL */
36 uint32_t gL1CacheEnabled = 1;
37
38 #define CACHE_COUNT CATEGORY_COUNT
39 #define CACHE_MAX 20
40
41 typedef struct
42 {
43 /* XXX should be mutex */
44 OSSpinLock lock;
45 int head;
46 si_item_t *item[CACHE_MAX];
47 si_list_t *list;
48 } cache_store_t;
49
50 typedef struct
51 {
52 cache_store_t cache_store[CACHE_COUNT];
53 } cache_si_private_t;
54
55 static void *
56 cache_validate_item(cache_si_private_t *pp, int cat, int where)
57 {
58 si_item_t *item;
59
60 item = pp->cache_store[cat].item[where];
61 if (item == NULL) return NULL;
62
63 if (si_item_is_valid(item)) return si_item_retain(item);
64
65 si_item_release(item);
66 pp->cache_store[cat].item[where] = NULL;
67
68 return NULL;
69 }
70
71 static void *
72 cache_validate_list(cache_si_private_t *pp, int cat)
73 {
74 uint32_t i, valid;
75 si_item_t *item, *last;
76 si_list_t *list;
77
78 list = pp->cache_store[cat].list;
79
80 if (list == NULL) return NULL;
81 if (list->count == 0) return NULL;
82
83 last = list->entry[0];
84 valid = si_item_is_valid(last);
85
86 for (i = 1; (i < list->count) && (valid == 1); i++)
87 {
88 item = list->entry[i];
89 if ((item->src == last->src) && (item->type == last->type) && (item->validation_a == last->validation_a) && (item->validation_b == last->validation_b)) continue;
90
91 last = item;
92 valid = si_item_is_valid(last);
93 }
94
95 if (valid) return si_list_retain(list);
96
97 si_list_release(list);
98 pp->cache_store[cat].list = NULL;
99
100 return NULL;
101 }
102
103 static si_item_t *
104 cache_fetch_item(si_mod_t *si, int cat, const char *name, uint32_t num, int which)
105 {
106 int i;
107 cache_si_private_t *pp;
108 si_item_t *item;
109
110 if (si == NULL) return NULL;
111 if (gL1CacheEnabled == 0) return NULL;
112
113 pp = (cache_si_private_t *)si->private;
114 if (pp == NULL) return NULL;
115
116 OSSpinLockLock(&(pp->cache_store[cat].lock));
117
118 for (i = 0; i < CACHE_MAX; i++)
119 {
120 item = cache_validate_item(pp, cat, i);
121 if (item && si_item_match(item, cat, name, num, which))
122 {
123 break;
124 }
125 else
126 {
127 si_item_release(item);
128 item = NULL;
129 }
130 }
131
132 OSSpinLockUnlock(&(pp->cache_store[cat].lock));
133
134 return item;
135 }
136
137 static si_list_t *
138 cache_fetch_list(si_mod_t *si, int cat)
139 {
140 cache_si_private_t *pp;
141 si_list_t *list;
142
143 if (si == NULL) return NULL;
144 if (gL1CacheEnabled == 0) return NULL;
145
146 pp = (cache_si_private_t *)si->private;
147 if (pp == NULL) return NULL;
148
149 OSSpinLockLock(&(pp->cache_store[cat].lock));
150 list = cache_validate_list(pp, cat);
151 OSSpinLockUnlock(&(pp->cache_store[cat].lock));
152
153 return list;
154 }
155
156 __private_extern__ si_item_t *
157 cache_user_byname(si_mod_t *si, const char *name)
158 {
159 return cache_fetch_item(si, CATEGORY_USER, name, 0, SEL_NAME);
160 }
161
162 __private_extern__ si_item_t *
163 cache_user_byuid(si_mod_t *si, uid_t uid)
164 {
165 return cache_fetch_item(si, CATEGORY_USER, NULL, uid, SEL_NUMBER);
166 }
167
168 __private_extern__ si_list_t *
169 cache_user_all(si_mod_t *si)
170 {
171 return cache_fetch_list(si, CATEGORY_USER);
172 }
173
174 __private_extern__ si_item_t *
175 cache_group_byname(si_mod_t *si, const char *name)
176 {
177 return cache_fetch_item(si, CATEGORY_GROUP, name, 0, SEL_NAME);
178 }
179
180 __private_extern__ si_item_t *
181 cache_group_bygid(si_mod_t *si, gid_t gid)
182 {
183 return cache_fetch_item(si, CATEGORY_GROUP, NULL, gid, SEL_NUMBER);
184 }
185
186 __private_extern__ si_list_t *
187 cache_group_all(si_mod_t *si)
188 {
189 return cache_fetch_list(si, CATEGORY_GROUP);
190 }
191
192 __private_extern__ si_item_t *
193 cache_grouplist(si_mod_t *si, const char *name)
194 {
195 return cache_fetch_item(si, CATEGORY_GROUPLIST, name, 0, SEL_NAME);
196 }
197
198 __private_extern__ si_item_t *
199 cache_alias_byname(si_mod_t *si, const char *name)
200 {
201 return cache_fetch_item(si, CATEGORY_ALIAS, name, 0, SEL_NAME);
202 }
203
204 __private_extern__ si_list_t *
205 cache_alias_all(si_mod_t *si)
206 {
207 return cache_fetch_list(si, CATEGORY_ALIAS);
208 }
209
210 __private_extern__ si_item_t *
211 cache_host_byname(si_mod_t *si, const char *name, int af, uint32_t *err)
212 {
213 si_item_t *item;
214
215 if (err != NULL) *err = SI_STATUS_NO_ERROR;
216 item = NULL;
217
218 if (af == AF_INET) item = cache_fetch_item(si, CATEGORY_HOST_IPV4, name, af, SEL_NAME);
219 else item = cache_fetch_item(si, CATEGORY_HOST_IPV6, name, af, SEL_NAME);
220
221 if ((item == NULL) && (err != NULL) && (*err == 0)) *err = SI_STATUS_H_ERRNO_HOST_NOT_FOUND;
222
223 return item;
224 }
225
226 __private_extern__ si_item_t *
227 cache_host_byaddr(si_mod_t *si, const void *addr, int af, uint32_t *err)
228 {
229 si_item_t *item;
230
231 if (err != NULL) *err = SI_STATUS_NO_ERROR;
232 item = NULL;
233
234 if (af == AF_INET) item = cache_fetch_item(si, CATEGORY_HOST_IPV4, addr, af, SEL_NUMBER);
235 else item = cache_fetch_item(si, CATEGORY_HOST_IPV6, addr, af, SEL_NUMBER);
236
237 if ((item == NULL) && (err != NULL) && (*err == 0)) *err = SI_STATUS_H_ERRNO_HOST_NOT_FOUND;
238
239 return item;
240 }
241
242 __private_extern__ si_list_t *
243 cache_host_all(si_mod_t *si)
244 {
245 return cache_fetch_list(si, CATEGORY_HOST);
246 }
247
248 __private_extern__ si_item_t *
249 cache_network_byname(si_mod_t *si, const char *name)
250 {
251 return cache_fetch_item(si, CATEGORY_NETWORK, name, 0, SEL_NAME);
252 }
253
254 __private_extern__ si_item_t *
255 cache_network_byaddr(si_mod_t *si, uint32_t addr)
256 {
257 return cache_fetch_item(si, CATEGORY_NETWORK, NULL, addr, SEL_NUMBER);
258 }
259
260 __private_extern__ si_list_t *
261 cache_network_all(si_mod_t *si)
262 {
263 return cache_fetch_list(si, CATEGORY_NETWORK);
264 }
265
266 __private_extern__ si_item_t *
267 cache_service_byname(si_mod_t *si, const char *name, const char *proto)
268 {
269 uint32_t pn;
270
271 if (name == NULL) return NULL;
272 if (proto == NULL) return cache_fetch_item(si, CATEGORY_SERVICE, name, 0, SEL_NAME);
273
274 pn = 1;
275 if (string_equal(proto, "tcp")) pn = 2;
276
277 return cache_fetch_item(si, CATEGORY_SERVICE, name, pn, SEL_NAME);
278 }
279
280 __private_extern__ si_item_t *
281 cache_service_byport(si_mod_t *si, int port, const char *proto)
282 {
283 return cache_fetch_item(si, CATEGORY_SERVICE, proto, port, SEL_NUMBER);
284 }
285
286 __private_extern__ si_list_t *
287 cache_service_all(si_mod_t *si)
288 {
289 return cache_fetch_list(si, CATEGORY_SERVICE);
290 }
291
292 __private_extern__ si_item_t *
293 cache_protocol_byname(si_mod_t *si, const char *name)
294 {
295 return cache_fetch_item(si, CATEGORY_PROTOCOL, name, 0, SEL_NAME);
296 }
297
298 __private_extern__ si_item_t *
299 cache_protocol_bynumber(si_mod_t *si, int number)
300 {
301 return cache_fetch_item(si, CATEGORY_PROTOCOL, NULL, number, SEL_NUMBER);
302 }
303
304 __private_extern__ si_list_t *
305 cache_protocol_all(si_mod_t *si)
306 {
307 return cache_fetch_list(si, CATEGORY_PROTOCOL);
308 }
309
310 __private_extern__ si_item_t *
311 cache_rpc_byname(si_mod_t *si, const char *name)
312 {
313 return cache_fetch_item(si, CATEGORY_RPC, name, 0, SEL_NAME);
314 }
315
316 __private_extern__ si_item_t *
317 cache_rpc_bynumber(si_mod_t *si, int number)
318 {
319 return cache_fetch_item(si, CATEGORY_RPC, NULL, number, SEL_NUMBER);
320 }
321
322 __private_extern__ si_list_t *
323 cache_rpc_all(si_mod_t *si)
324 {
325 return cache_fetch_list(si, CATEGORY_RPC);
326 }
327
328 __private_extern__ si_item_t *
329 cache_fs_byspec(si_mod_t *si, const char *name)
330 {
331 return cache_fetch_item(si, CATEGORY_FS, name, 0, SEL_NAME);
332 }
333
334 __private_extern__ si_item_t *
335 cache_fs_byfile(si_mod_t *si, const char *name)
336 {
337 return cache_fetch_item(si, CATEGORY_FS, name, 0, SEL_NUMBER);
338 }
339
340 __private_extern__ si_list_t *
341 cache_fs_all(si_mod_t *si)
342 {
343 return cache_fetch_list(si, CATEGORY_FS);
344 }
345
346 __private_extern__ si_item_t *
347 cache_mac_byname(si_mod_t *si, const char *name)
348 {
349 return cache_fetch_item(si, CATEGORY_MAC, name, 0, SEL_NAME);
350 }
351
352 __private_extern__ si_item_t *
353 cache_mac_bymac(si_mod_t *si, const char *mac)
354 {
355 return cache_fetch_item(si, CATEGORY_MAC, mac, 0, SEL_NUMBER);
356 }
357
358 __private_extern__ si_list_t *
359 cache_mac_all(si_mod_t *si)
360 {
361 return cache_fetch_list(si, CATEGORY_MAC);
362 }
363
364 __private_extern__ si_item_t *
365 cache_nameinfo(si_mod_t *si, const struct sockaddr *sa, int flags, uint32_t *err)
366 {
367 /*
368 * Caching of getnameinfo(3) is not supported.
369 * Only the individual host_byaddr and serv_byaddr responses will be cached.
370 * This is because getnameinfo(3) returns numeric responses instead of
371 * failing, which would poison the cache.
372 */
373 if (err) *err = SI_STATUS_H_ERRNO_HOST_NOT_FOUND;
374 return NULL;
375 }
376
377 __private_extern__ void
378 cache_close(si_mod_t *si)
379 {
380 cache_si_private_t *pp;
381 int i, j;
382
383 if (si == NULL) return;
384
385 pp = (cache_si_private_t *)si->private;
386 if (pp == NULL) return;
387
388 for (i = 0; i < CACHE_COUNT; i++)
389 {
390 si_list_release(pp->cache_store[i].list);
391
392 for (j = 0; j < CACHE_MAX; j++)
393 {
394 si_item_release(pp->cache_store[i].item[j]);
395 pp->cache_store[i].item[j] = NULL;
396 }
397 }
398
399 free(si->private);
400 }
401
402 __private_extern__ si_mod_t *
403 si_module_static_cache()
404 {
405 si_mod_t *out;
406 char *outname;
407 cache_si_private_t *pp;
408
409 out = (si_mod_t *)calloc(1, sizeof(si_mod_t));
410 outname = strdup("cache");
411 pp = (cache_si_private_t *)calloc(1, sizeof(cache_si_private_t));
412
413 if ((out == NULL) || (outname == NULL) || (pp == NULL))
414 {
415 if (out != NULL) free(out);
416 if (outname != NULL) free(outname);
417 if (pp != NULL) free(pp);
418
419 errno = ENOMEM;
420 return NULL;
421 }
422
423 out->name = outname;
424 out->vers = 1;
425 out->refcount = 1;
426 out->private = pp;
427
428 out->sim_close = cache_close;
429
430 out->sim_user_byname = cache_user_byname;
431 out->sim_user_byuid = cache_user_byuid;
432 out->sim_user_all = cache_user_all;
433
434 out->sim_group_byname = cache_group_byname;
435 out->sim_group_bygid = cache_group_bygid;
436 out->sim_group_all = cache_group_all;
437
438 out->sim_grouplist = cache_grouplist;
439
440 /* no netgroup support */
441 out->sim_netgroup_byname = NULL;
442 out->sim_in_netgroup = NULL;
443
444 out->sim_alias_byname = cache_alias_byname;
445 out->sim_alias_all = cache_alias_all;
446
447 out->sim_host_byname = cache_host_byname;
448 out->sim_host_byaddr = cache_host_byaddr;
449 out->sim_host_all = cache_host_all;
450
451 out->sim_network_byname = cache_network_byname;
452 out->sim_network_byaddr = cache_network_byaddr;
453 out->sim_network_all = cache_network_all;
454
455 out->sim_service_byname = cache_service_byname;
456 out->sim_service_byport = cache_service_byport;
457 out->sim_service_all = cache_service_all;
458
459 out->sim_protocol_byname = cache_protocol_byname;
460 out->sim_protocol_bynumber = cache_protocol_bynumber;
461 out->sim_protocol_all = cache_protocol_all;
462
463 out->sim_rpc_byname = cache_rpc_byname;
464 out->sim_rpc_bynumber = cache_rpc_bynumber;
465 out->sim_rpc_all = cache_rpc_all;
466
467 out->sim_fs_byspec = cache_fs_byspec;
468 out->sim_fs_byfile = cache_fs_byfile;
469 out->sim_fs_all = cache_fs_all;
470
471 out->sim_mac_byname = cache_mac_byname;
472 out->sim_mac_bymac = cache_mac_bymac;
473 out->sim_mac_all = cache_mac_all;
474
475 /* no addrinfo support */
476 out->sim_wants_addrinfo = NULL;
477 out->sim_addrinfo = NULL;
478
479 /* no nameinfo support */
480 out->sim_nameinfo = cache_nameinfo;
481
482 return out;
483 }
484
485 __private_extern__ void
486 si_cache_add_item(si_mod_t *si, si_mod_t *src, si_item_t *item)
487 {
488 cache_si_private_t *pp;
489 int head, cat;
490
491 if (si == NULL) return;
492 if (src == NULL) return;
493 if (item == NULL) return;
494
495 if (si == src) return;
496
497 if (src->name == NULL) return;
498 if (string_equal(src->name, "cache")) return;
499
500 cat = item->type;
501 if ((cat < 0) || (cat >= CACHE_COUNT)) return;
502
503 pp = (cache_si_private_t *)si->private;
504 if (pp == NULL) return;
505
506 OSSpinLockLock(&(pp->cache_store[cat].lock));
507
508 head = pp->cache_store[item->type].head;
509
510 si_item_release(pp->cache_store[item->type].item[head]);
511 pp->cache_store[item->type].item[head] = si_item_retain(item);
512
513 head++;
514 if (head >= CACHE_MAX) head = 0;
515 pp->cache_store[item->type].head = head;
516
517 OSSpinLockUnlock(&(pp->cache_store[cat].lock));
518 }
519
520 __private_extern__ void
521 si_cache_add_list(si_mod_t *si, si_mod_t *src, si_list_t *list)
522 {
523 cache_si_private_t *pp;
524 si_item_t *item;
525 int cat;
526
527 if (si == NULL) return;
528 if (src == NULL) return;
529 if (list == NULL) return;
530 if (list->count == 0) return;
531
532 if (si == src) return;
533
534 if (src->name == NULL) return;
535 if (string_equal(src->name, "cache")) return;
536
537 item = list->entry[0];
538 if (item == NULL) return;
539
540 cat = item->type;
541 if ((cat < 0) || (cat >= CACHE_COUNT)) return;
542
543 pp = (cache_si_private_t *)si->private;
544 if (pp == NULL) return;
545
546 OSSpinLockLock(&(pp->cache_store[cat].lock));
547
548 si_list_release(pp->cache_store[item->type].list);
549 pp->cache_store[item->type].list = si_list_retain(list);
550
551 OSSpinLockUnlock(&(pp->cache_store[cat].lock));
552 }