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