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