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 <stdlib.h>
25#include <paths.h>
26#include <stdarg.h>
27#include <unistd.h>
28#include <stdio.h>
29#include <string.h>
30#include <errno.h>
31#include <netdb.h>
32#include <pthread.h>
33#include <sys/param.h>
34#include <sys/stat.h>
35#include <arpa/inet.h>
36#include <dispatch/dispatch.h>
37#include "si_module.h"
38
39#define _PATH_SI_CONF "/etc/sysinfo.conf"
40
41#define SEARCH_FLAG_CACHE_ENABLED 0x00000001
42#define SEARCH_MODULE_FLAG_DISABLED 0x00000001
43
44typedef struct
45{
46	si_mod_t **module;
47	uint32_t *module_flags;
48	uint32_t count;
49	uint32_t flags;
50} search_list_t;
51
52typedef struct
53{
54	search_list_t search_list[CATEGORY_COUNT];
55	si_mod_t *cache;
56} search_si_private_t;
57
58extern void si_cache_add_item(si_mod_t *si, si_mod_t *src, si_item_t *item);
59extern void si_cache_add_list(si_mod_t *si, si_mod_t *src, si_list_t *list);
60
61extern char **_fsi_tokenize(char *data, const char *sep, int trailing_empty, int *ntokens);
62extern char *_fsi_get_line(FILE *fp);
63
64static void si_module_config_parse_line(search_si_private_t *pp, char *line);
65static void si_module_config_modules_for_category(search_si_private_t *pp, int cat, int ntokens, const char * const *tokens);
66
67static si_mod_t *
68search_get_module(search_si_private_t *pp, int cat, int *n)
69{
70	int x;
71
72	if ((pp == NULL) || (n == NULL)) return NULL;
73
74	x = *n;
75	*n = x + 1;
76
77	/* Use custom search list if available */
78	if (pp->search_list[cat].count > 0 && x < pp->search_list[cat].count)
79	{
80		return pp->search_list[cat].module[x];
81	}
82
83	/* Otherwise use the default search list */
84	while (x < pp->search_list[CATEGORY_DEFAULT].count)
85	{
86		if (pp->search_list[CATEGORY_DEFAULT].module_flags[x] & SEARCH_MODULE_FLAG_DISABLED)
87		{
88			x++;
89			*n = x + 1;
90		}
91		else
92		{
93			return pp->search_list[CATEGORY_DEFAULT].module[x];
94		}
95	}
96
97	return NULL;
98}
99
100__private_extern__ void
101search_set_flags(si_mod_t *si, const char *name, uint32_t flag)
102{
103	search_si_private_t *pp;
104	uint32_t i;
105
106	if (si == NULL) return;
107	if (si->private == NULL) return;
108
109	pp = (search_si_private_t *)si->private;
110
111	for (i = 0; i < pp->search_list[CATEGORY_DEFAULT].count; i++)
112	{
113		si_mod_t *mod = pp->search_list[CATEGORY_DEFAULT].module[i];
114		if ((mod == NULL) || (mod->name == NULL)) continue;
115
116		if (string_equal(name, mod->name))
117		{
118			pp->search_list[CATEGORY_DEFAULT].module_flags[i] = flag;
119			break;
120		}
121	}
122}
123
124static si_mod_t *
125search_cat_cache(search_si_private_t *pp, int cat)
126{
127	if (pp == NULL) return NULL;
128	if (cat < 0 || cat > CATEGORY_COUNT) return NULL;
129
130	if (pp->search_list[cat].count == 0)
131	{
132		cat = CATEGORY_DEFAULT;
133	}
134
135	if ((pp->search_list[cat].flags & SEARCH_FLAG_CACHE_ENABLED) != 0)
136	{
137		return pp->cache;
138	}
139
140	return NULL;
141}
142
143static void
144search_close(si_mod_t *si)
145{
146	int i;
147	search_si_private_t *pp;
148
149	if (si == NULL) return;
150	if (si->private == NULL) return;
151
152	pp = (search_si_private_t *)si->private;
153
154	for (i = 0; i < CATEGORY_COUNT; i++)
155	{
156		if (pp->search_list[i].module != NULL)
157		{
158			free(pp->search_list[i].module);
159			pp->search_list[i].module = NULL;
160			pp->search_list[i].count = 0;
161			pp->search_list[i].flags = 0;
162		}
163	}
164
165	free(pp);
166}
167
168static si_item_t *
169search_item_byname(si_mod_t *si, const char *name, int cat, si_item_t *(*call)(si_mod_t *, const char *))
170{
171	int i;
172	search_si_private_t *pp;
173	si_item_t *item;
174	si_mod_t *src;
175
176	if (si == NULL) return NULL;
177	if (call == NULL) return NULL;
178
179	pp = (search_si_private_t *)si->private;
180	if (pp == NULL) return NULL;
181
182	i = 0;
183
184	while (NULL != (src = search_get_module(pp, cat, &i)))
185	{
186		item = call(src, name);
187		if (item != NULL)
188		{
189			si_cache_add_item(search_cat_cache(pp, cat), src, item);
190			return item;
191		}
192	}
193
194	return NULL;
195}
196
197static si_item_t *
198search_item_bynumber(si_mod_t *si, uint32_t number, int cat, si_item_t *(*call)(si_mod_t *, uint32_t))
199{
200	int i;
201	search_si_private_t *pp;
202	si_item_t *item;
203	si_mod_t *src;
204
205	if (si == NULL) return NULL;
206	if (call == NULL) return NULL;
207
208	pp = (search_si_private_t *)si->private;
209	if (pp == NULL) return NULL;
210
211	i = 0;
212
213	while (NULL != (src = search_get_module(pp, cat, &i)))
214	{
215		item = call(src, number);
216		if (item != NULL)
217		{
218			si_cache_add_item(search_cat_cache(pp, cat), src, item);
219			return item;
220		}
221	}
222
223	return NULL;
224}
225
226static si_item_t *
227search_item_byuuid(si_mod_t *si, uuid_t uuid, int cat, si_item_t *(*call)(si_mod_t *, uuid_t))
228{
229	int i;
230	search_si_private_t *pp;
231	si_item_t *item;
232	si_mod_t *src;
233
234	if (si == NULL) return NULL;
235	if (call == NULL) return NULL;
236
237	pp = (search_si_private_t *)si->private;
238	if (pp == NULL) return NULL;
239
240	i = 0;
241
242	while (NULL != (src = search_get_module(pp, cat, &i)))
243	{
244		item = call(src, uuid);
245		if (item != NULL)
246		{
247			/*
248			 * N.B. item not added to cache, since the data does not
249			 * contain the uuid that was used to find it.
250			 */
251			return item;
252		}
253	}
254
255	return NULL;
256}
257static si_list_t *
258search_list(si_mod_t *si, int cat, si_list_t *(*call)(si_mod_t *))
259{
260	int i, null_res;
261	search_si_private_t *pp;
262	si_list_t *list, *all;
263	si_mod_t *cache, *src;
264
265	if (si == NULL) return NULL;
266	if (call == NULL) return NULL;
267
268	pp = (search_si_private_t *)si->private;
269	if (pp == NULL) return NULL;
270
271	cache = search_cat_cache(pp, cat);
272	if (cache != NULL)
273	{
274		list = call(cache);
275		if (list != NULL) return list;
276	}
277
278	i = 0;
279
280	all = NULL;
281	null_res = 0;
282
283	while (NULL != (src = search_get_module(pp, cat, &i)))
284	{
285		if (src == pp->cache) continue;
286
287		list = call(src);
288		if (list == NULL)
289		{
290			null_res = 1;
291			continue;
292		}
293
294		all = si_list_concat(all, list);
295		si_list_release(list);
296	}
297
298	if ((all != NULL) && (null_res == 0)) si_cache_add_list(cache, si, all);
299	return all;
300}
301
302static si_item_t *
303search_user_byname(si_mod_t *si, const char *name)
304{
305	return search_item_byname(si, name, CATEGORY_USER, si_user_byname);
306}
307
308static si_item_t *
309search_user_byuid(si_mod_t *si, uid_t uid)
310{
311	return search_item_bynumber(si, (uint32_t)uid, CATEGORY_USER, si_user_byuid);
312}
313
314static si_item_t *
315search_user_byuuid(si_mod_t *si, uuid_t uuid)
316{
317	return search_item_byuuid(si, uuid, CATEGORY_USER, si_user_byuuid);
318}
319
320static si_list_t *
321search_user_all(si_mod_t *si)
322{
323	return search_list(si, CATEGORY_USER, si_user_all);
324}
325
326static si_item_t *
327search_group_byname(si_mod_t *si, const char *name)
328{
329	return search_item_byname(si, name, CATEGORY_GROUP, si_group_byname);
330}
331
332static si_item_t *
333search_group_bygid(si_mod_t *si, gid_t gid)
334{
335	return search_item_bynumber(si, (uint32_t)gid, CATEGORY_GROUP, si_group_bygid);
336}
337
338static si_item_t *
339search_group_byuuid(si_mod_t *si, uuid_t uuid)
340{
341	return search_item_byuuid(si, uuid, CATEGORY_GROUP, si_group_byuuid);
342}
343
344static si_list_t *
345search_group_all(si_mod_t *si)
346{
347	return search_list(si, CATEGORY_GROUP, si_group_all);
348}
349
350static si_item_t *
351search_groupist(si_mod_t *si, const char *name, uint32_t count)
352{
353	int i;
354	search_si_private_t *pp;
355	si_item_t *item = NULL;
356	si_mod_t *src;
357
358	if (si == NULL) return NULL;
359
360	pp = (search_si_private_t *)si->private;
361	if (pp == NULL) return NULL;
362
363	i = 0;
364
365	while (NULL != (src = search_get_module(pp, CATEGORY_GROUPLIST, &i))) {
366		if (src == pp->cache) continue;
367
368		if (src->vtable->sim_grouplist != NULL) {
369			item = src->vtable->sim_grouplist(si, name, count);
370			if (item != NULL) break;
371		}
372	}
373
374	return item;
375}
376
377static si_list_t *
378search_netgroup_byname(si_mod_t *si, const char *name)
379{
380	int i, cat, null_res;
381	search_si_private_t *pp;
382	si_list_t *list, *all;
383	si_mod_t *cache, *src;
384
385	if (si == NULL) return NULL;
386
387	pp = (search_si_private_t *)si->private;
388	if (pp == NULL) return NULL;
389
390	cat = CATEGORY_NETGROUP;
391
392	cache = search_cat_cache(pp, cat);
393	if (cache != NULL)
394	{
395		list = si_netgroup_byname(cache, name);
396		if (list != NULL) return list;
397	}
398
399	i = 0;
400	null_res = 0;
401
402	all = NULL;
403	while (NULL != (src = search_get_module(pp, cat, &i)))
404	{
405		if (src == pp->cache) continue;
406
407		list = si_netgroup_byname(src, name);
408		if (list == NULL)
409		{
410			null_res = 1;
411			continue;
412		}
413
414		all = si_list_concat(all, list);
415		si_list_release(list);
416	}
417
418	if ((all != NULL) && (null_res == 0)) si_cache_add_list(cache, si, all);
419	return all;
420}
421
422static int
423search_in_netgroup(si_mod_t *si, const char *group, const char *host, const char *user, const char *domain)
424{
425	int i, cat, innetgr;
426	search_si_private_t *pp;
427	si_mod_t *src;
428
429	if (si == NULL) return 0;
430
431	pp = (search_si_private_t *)si->private;
432	if (pp == NULL) return 0;
433
434	cat = CATEGORY_NETGROUP;
435	i = 0;
436
437	while (NULL != (src = search_get_module(pp, cat, &i)))
438	{
439		innetgr = si_in_netgroup(src, group, host, user, domain);
440		if (innetgr != 0) return 1;
441	}
442
443	return 0;
444}
445
446static si_item_t *
447search_alias_byname(si_mod_t *si, const char *name)
448{
449	return search_item_byname(si, name, CATEGORY_ALIAS, si_alias_byname);
450}
451
452static si_list_t *
453search_alias_all(si_mod_t *si)
454{
455	return search_list(si, CATEGORY_ALIAS, si_alias_all);
456}
457
458static si_item_t *
459search_host_byname(si_mod_t *si, const char *name, int af, const char *interface, uint32_t *err)
460{
461	int i, cat;
462	search_si_private_t *pp;
463	si_item_t *item;
464	si_mod_t *src;
465
466	if (err != NULL) *err = SI_STATUS_NO_ERROR;
467
468	if ((si == NULL) || (name == NULL))
469	{
470		if (err != NULL) *err = SI_STATUS_H_ERRNO_NO_RECOVERY;
471		return NULL;
472	}
473
474	pp = (search_si_private_t *)si->private;
475	if (pp == NULL)
476	{
477		if (err != NULL) *err = SI_STATUS_H_ERRNO_NO_RECOVERY;
478		return NULL;
479	}
480
481	cat = CATEGORY_HOST_IPV4;
482	if (af == AF_INET6) cat = CATEGORY_HOST_IPV6;
483
484	i = 0;
485
486	while (NULL != (src = search_get_module(pp, cat, &i)))
487	{
488		item = si_host_byname(src, name, af, interface, err);
489		if (item != NULL)
490		{
491			si_cache_add_item(search_cat_cache(pp, cat), src, item);
492			return item;
493		}
494	}
495
496	if (err != NULL) *err = SI_STATUS_H_ERRNO_HOST_NOT_FOUND;
497	return NULL;
498}
499
500static si_item_t *
501search_host_byaddr(si_mod_t *si, const void *addr, int af, const char *interface, uint32_t *err)
502{
503	int i, cat;
504	search_si_private_t *pp;
505	si_item_t *item;
506	si_mod_t *src;
507
508	if (err != NULL) *err = SI_STATUS_NO_ERROR;
509
510	if ((si == NULL) || (addr == NULL))
511	{
512		if (err != NULL) *err = SI_STATUS_H_ERRNO_NO_RECOVERY;
513		return NULL;
514	}
515
516	pp = (search_si_private_t *)si->private;
517	if (pp == NULL)
518	{
519		if (err != NULL) *err = SI_STATUS_H_ERRNO_NO_RECOVERY;
520		return NULL;
521	}
522
523	cat = CATEGORY_HOST_IPV4;
524	if (af == AF_INET6) cat = CATEGORY_HOST_IPV6;
525
526	i = 0;
527
528	while (NULL != (src = search_get_module(pp, cat, &i)))
529	{
530		item = si_host_byaddr(src, addr, af, interface, err);
531		if (item != NULL)
532		{
533			si_cache_add_item(search_cat_cache(pp, cat), src, item);
534			return item;
535		}
536	}
537
538	if (err != NULL) *err = SI_STATUS_H_ERRNO_HOST_NOT_FOUND;
539	return NULL;
540}
541
542static si_list_t *
543search_host_all(si_mod_t *si)
544{
545	return search_list(si, CATEGORY_HOST, si_host_all);
546}
547
548static si_item_t *
549search_network_byname(si_mod_t *si, const char *name)
550{
551	return search_item_byname(si, name, CATEGORY_NETWORK, si_network_byname);
552}
553
554static si_item_t *
555search_network_byaddr(si_mod_t *si, uint32_t addr)
556{
557	return search_item_bynumber(si, addr, CATEGORY_NETWORK, si_network_byaddr);
558}
559
560static si_list_t *
561search_network_all(si_mod_t *si)
562{
563	return search_list(si, CATEGORY_NETWORK, si_network_all);
564}
565
566static si_item_t *
567search_service_byname(si_mod_t *si, const char *name, const char *proto)
568{
569	int i, cat;
570	si_item_t *item;
571	search_si_private_t *pp;
572	si_mod_t *src;
573
574	if (si == NULL) return NULL;
575	if (name == NULL) return NULL;
576
577	pp = (search_si_private_t *)si->private;
578	if (pp == NULL) return NULL;
579
580	cat = CATEGORY_SERVICE;
581	i = 0;
582
583	while (NULL != (src = search_get_module(pp, cat, &i)))
584	{
585		item = si_service_byname(src, name, proto);
586		if (item != NULL)
587		{
588			si_cache_add_item(search_cat_cache(pp, cat), src, item);
589			return item;
590		}
591	}
592
593	return NULL;
594}
595
596static si_item_t *
597search_service_byport(si_mod_t *si, int port, const char *proto)
598{
599	int i, cat;
600	search_si_private_t *pp;
601	si_item_t *item;
602	si_mod_t *src;
603
604	if (si == NULL) return NULL;
605
606	pp = (search_si_private_t *)si->private;
607	if (pp == NULL) return NULL;
608
609	cat = CATEGORY_SERVICE;
610	i = 0;
611
612	while (NULL != (src = search_get_module(pp, cat, &i)))
613	{
614		item = si_service_byport(src, port, proto);
615		if (item != NULL)
616		{
617			si_cache_add_item(search_cat_cache(pp, cat), src, item);
618			return item;
619		}
620	}
621
622	return NULL;
623}
624
625static si_list_t *
626search_service_all(si_mod_t *si)
627{
628	return search_list(si, CATEGORY_SERVICE, si_service_all);
629}
630
631static si_item_t *
632search_protocol_byname(si_mod_t *si, const char *name)
633{
634	return search_item_byname(si, name, CATEGORY_PROTOCOL, si_protocol_byname);
635}
636
637static si_item_t *
638search_protocol_bynumber(si_mod_t *si, int number)
639{
640	return search_item_bynumber(si, (uint32_t)number, CATEGORY_PROTOCOL, si_protocol_bynumber);
641}
642
643static si_list_t *
644search_protocol_all(si_mod_t *si)
645{
646	return search_list(si, CATEGORY_PROTOCOL, si_protocol_all);
647}
648
649static si_item_t *
650search_rpc_byname(si_mod_t *si, const char *name)
651{
652	return search_item_byname(si, name, CATEGORY_RPC, si_rpc_byname);
653}
654
655static si_item_t *
656search_rpc_bynumber(si_mod_t *si, int number)
657{
658	int i, cat;
659	search_si_private_t *pp;
660	si_item_t *item;
661	si_mod_t *src;
662
663	if (si == NULL) return NULL;
664
665	pp = (search_si_private_t *)si->private;
666	if (pp == NULL) return NULL;
667
668	cat = CATEGORY_RPC;
669	i = 0;
670
671	while (NULL != (src = search_get_module(pp, cat, &i)))
672	{
673		item = si_rpc_bynumber(src, number);
674		if (item != NULL)
675		{
676			si_cache_add_item(search_cat_cache(pp, cat), src, item);
677			return item;
678		}
679	}
680
681	return NULL;
682}
683
684static si_list_t *
685search_rpc_all(si_mod_t *si)
686{
687	return search_list(si, CATEGORY_RPC, si_rpc_all);
688}
689
690static si_item_t *
691search_fs_byspec(si_mod_t *si, const char *name)
692{
693	return search_item_byname(si, name, CATEGORY_FS, si_fs_byspec);
694}
695
696static si_item_t *
697search_fs_byfile(si_mod_t *si, const char *name)
698{
699	return search_item_byname(si, name, CATEGORY_FS, si_fs_byfile);
700}
701
702static si_list_t *
703search_fs_all(si_mod_t *si)
704{
705	return search_list(si, CATEGORY_FS, si_fs_all);
706}
707
708static si_item_t *
709search_mac_byname(si_mod_t *si, const char *name)
710{
711	return search_item_byname(si, name, CATEGORY_MAC, si_mac_byname);
712}
713
714static si_item_t *
715search_mac_bymac(si_mod_t *si, const char *mac)
716{
717	return search_item_byname(si, mac, CATEGORY_MAC, si_mac_bymac);
718}
719
720static si_list_t *
721search_mac_all(si_mod_t *si)
722{
723	return search_list(si, CATEGORY_MAC, si_mac_all);
724}
725
726static si_list_t *
727search_srv_byname(si_mod_t *si, const char* qname, const char *interface, uint32_t *err)
728{
729	int i, cat;
730	si_list_t *list = NULL;
731	si_mod_t *src;
732	search_si_private_t *pp;
733
734	if (si == NULL) return NULL;
735
736	pp = (search_si_private_t *)si->private;
737	if (pp == NULL) return NULL;
738
739	cat = CATEGORY_SRV;
740	i = 0;
741
742	while (NULL != (src = search_get_module(pp, cat, &i)))
743	{
744		if (src == pp->cache) continue;
745
746		if (src->vtable->sim_srv_byname != NULL)
747		{
748			list = src->vtable->sim_srv_byname(src, qname, interface, err);
749			if (list != NULL) return list;
750		}
751	}
752
753	if ((i > 0) && (err != NULL)) *err = SI_STATUS_EAI_NONAME;
754	return NULL;
755}
756
757static int
758search_wants_addrinfo(si_mod_t *si)
759{
760	int i, cat;
761	si_mod_t *src;
762	search_si_private_t *pp;
763
764	if (si == NULL) return 0;
765
766	pp = (search_si_private_t *)si->private;
767	if (pp == NULL) return 0;
768
769	cat = CATEGORY_ADDRINFO;
770	i = 0;
771
772	while (NULL != (src = search_get_module(pp, cat, &i)))
773	{
774		if (src == pp->cache) continue;
775		if (src->vtable->sim_addrinfo != NULL) return 1;
776	}
777
778	return 0;
779}
780
781static si_list_t *
782search_addrinfo(si_mod_t *si, const void *node, const void *serv, uint32_t family, uint32_t socktype, uint32_t protocol, uint32_t flags, const char *interface, uint32_t *err)
783{
784	int i, cat;
785	search_si_private_t *pp;
786	si_list_t *list = NULL;
787	si_mod_t *src;
788
789	if (err != NULL) *err = SI_STATUS_EAI_FAIL;
790
791	if (si == NULL) return NULL;
792
793	pp = (search_si_private_t *)si->private;
794	if (pp == NULL) return NULL;
795
796	cat = CATEGORY_ADDRINFO;
797	i = 0;
798
799	while (NULL != (src = search_get_module(pp, cat, &i)))
800	{
801		if (src == pp->cache) continue;
802
803		if (src->vtable->sim_addrinfo != NULL)
804		{
805			list = src->vtable->sim_addrinfo(src, node, serv, family, socktype, protocol, flags, interface, err);
806			if (list != NULL) return list;
807		}
808	}
809
810	if ((i > 0) && (err != NULL)) *err = SI_STATUS_EAI_NONAME;
811	return NULL;
812}
813
814static si_item_t *
815search_nameinfo(si_mod_t *si, const struct sockaddr *sa, int flags, const char *interface, uint32_t *err)
816{
817	int i, cat;
818	search_si_private_t *pp;
819	si_item_t *item;
820	si_mod_t *src;
821
822	if (err != NULL) *err = SI_STATUS_EAI_FAIL;
823
824	if (si == NULL) return NULL;
825
826	pp = (search_si_private_t *)si->private;
827	if (pp == NULL) return NULL;
828
829	cat = CATEGORY_NAMEINFO;
830	i = 0;
831
832	while (NULL != (src = search_get_module(pp, cat, &i)))
833	{
834		item = si_nameinfo(src, sa, flags, interface, err);
835		if (item != NULL)
836		{
837			si_cache_add_item(search_cat_cache(pp, cat), src, item);
838			if (err != NULL) *err = SI_STATUS_NO_ERROR;
839			return item;
840		}
841	}
842
843	if ((i > 0) && (err != NULL)) *err = SI_STATUS_EAI_NONAME;
844	return NULL;
845}
846
847static int
848search_is_valid(si_mod_t *si, si_item_t *item)
849{
850	si_mod_t *src;
851
852	if (si == NULL) return 0;
853	if (item == NULL) return 0;
854	if (si->name == NULL) return 0;
855	if (item->src == NULL) return 0;
856
857	src = (si_mod_t *)item->src;
858
859	if (src->name == NULL) return 0;
860	if (string_not_equal(si->name, src->name)) return 0;
861	return 0;
862}
863
864si_mod_t *
865si_module_static_search(void)
866{
867	static const struct si_mod_vtable_s search_vtable =
868	{
869		.sim_close = &search_close,
870
871		.sim_is_valid = &search_is_valid,
872
873		.sim_user_byname = &search_user_byname,
874		.sim_user_byuid = &search_user_byuid,
875		.sim_user_byuuid = &search_user_byuuid,
876		.sim_user_all = &search_user_all,
877
878		.sim_group_byname = &search_group_byname,
879		.sim_group_bygid = &search_group_bygid,
880		.sim_group_byuuid = &search_group_byuuid,
881		.sim_group_all = &search_group_all,
882
883		.sim_grouplist = &search_groupist,
884
885		.sim_netgroup_byname = &search_netgroup_byname,
886		.sim_in_netgroup = &search_in_netgroup,
887
888		.sim_alias_byname = &search_alias_byname,
889		.sim_alias_all = &search_alias_all,
890
891		.sim_host_byname = &search_host_byname,
892		.sim_host_byaddr = &search_host_byaddr,
893		.sim_host_all = &search_host_all,
894
895		.sim_network_byname = &search_network_byname,
896		.sim_network_byaddr = &search_network_byaddr,
897		.sim_network_all = &search_network_all,
898
899		.sim_service_byname = &search_service_byname,
900		.sim_service_byport = &search_service_byport,
901		.sim_service_all = &search_service_all,
902
903		.sim_protocol_byname = &search_protocol_byname,
904		.sim_protocol_bynumber = &search_protocol_bynumber,
905		.sim_protocol_all = &search_protocol_all,
906
907		.sim_rpc_byname = &search_rpc_byname,
908		.sim_rpc_bynumber = &search_rpc_bynumber,
909		.sim_rpc_all = &search_rpc_all,
910
911		.sim_fs_byspec = &search_fs_byspec,
912		.sim_fs_byfile = &search_fs_byfile,
913		.sim_fs_all = &search_fs_all,
914
915		.sim_mac_byname = &search_mac_byname,
916		.sim_mac_bymac = &search_mac_bymac,
917		.sim_mac_all = &search_mac_all,
918
919		.sim_addrinfo = &search_addrinfo,
920		.sim_wants_addrinfo = &search_wants_addrinfo,
921		.sim_nameinfo = &search_nameinfo,
922
923		.sim_srv_byname = &search_srv_byname,
924	};
925
926	static si_mod_t si =
927	{
928		.vers = 1,
929		.refcount = 1,
930		.flags = SI_MOD_FLAG_STATIC,
931
932		.private = NULL,
933		.vtable = &search_vtable,
934	};
935
936	static dispatch_once_t once;
937
938	dispatch_once(&once, ^{
939		si.name = strdup("search");
940		search_si_private_t *pp = calloc(1, sizeof(search_si_private_t));
941		si.private = pp;
942
943		/*
944		 * Default search order:
945		 * 1) cache
946		 * 2) DirectoryService/OpenDirectory (where available)
947		 * 3) flat file
948		 * 4) mDNSResponder
949		 */
950
951		const char * const modules[] =
952		{
953			"default", // CATEGORY_DEFAULT
954			"cache",
955#ifdef DS_AVAILABLE
956			"ds",
957#endif
958			"mdns",
959			"file",
960		};
961
962		int count = sizeof(modules) / sizeof(char *);
963		si_module_config_modules_for_category(pp, CATEGORY_DEFAULT, count, modules);
964		pp->cache = pp->search_list[CATEGORY_DEFAULT].module[0];
965
966		char *check = getenv("SYSINFO_CONF_ENABLE");
967		if ((check != NULL) && (!strcmp(check, "1")))
968		{
969			FILE *conf = fopen(_PATH_SI_CONF, "r");
970			errno = 0;
971			if (conf != NULL)
972			{
973				forever
974				{
975					char *line = _fsi_get_line(conf);
976					if (line == NULL) break;
977
978					si_module_config_parse_line(pp, line);
979					free(line);
980				}
981
982				fclose(conf);
983			}
984		}
985	});
986
987	return &si;
988}
989
990static void
991si_module_config_parse_line(search_si_private_t *pp, char *line)
992{
993	if (line == NULL || line[0] == '#') {
994		return;
995	}
996
997	int ntokens = 0;
998	char **tokens = _fsi_tokenize(line, "	: ", 0, &ntokens);
999
1000	int cat = CATEGORY_INVALID;
1001
1002	if (string_equal(tokens[0], "default")) cat = CATEGORY_DEFAULT;
1003	else if (string_equal(tokens[0], "user")) cat = CATEGORY_USER;
1004	else if (string_equal(tokens[0], "group")) cat = CATEGORY_GROUP;
1005	else if (string_equal(tokens[0], "grouplist")) cat = CATEGORY_GROUPLIST;
1006	else if (string_equal(tokens[0], "netgroup")) cat = CATEGORY_NETGROUP;
1007	else if (string_equal(tokens[0], "alias")) cat = CATEGORY_ALIAS;
1008	else if (string_equal(tokens[0], "host")) cat = CATEGORY_HOST_IPV4;
1009	else if (string_equal(tokens[0], "network")) cat = CATEGORY_NETWORK;
1010	else if (string_equal(tokens[0], "service")) cat = CATEGORY_SERVICE;
1011	else if (string_equal(tokens[0], "protocol")) cat = CATEGORY_PROTOCOL;
1012	else if (string_equal(tokens[0], "rpc")) cat = CATEGORY_RPC;
1013	else if (string_equal(tokens[0], "fs")) cat = CATEGORY_FS;
1014	else if (string_equal(tokens[0], "mac")) cat = CATEGORY_MAC;
1015	else if (string_equal(tokens[0], "addrinfo")) cat = CATEGORY_ADDRINFO;
1016	else if (string_equal(tokens[0], "nameinfo")) cat = CATEGORY_NAMEINFO;
1017
1018	if (cat != CATEGORY_INVALID)
1019	{
1020		si_module_config_modules_for_category(pp, cat, ntokens, (const char * const *)tokens);
1021	}
1022
1023	free(tokens);
1024}
1025
1026static void
1027si_module_config_modules_for_category(search_si_private_t *pp, int cat, int ntokens, const char * const *tokens)
1028{
1029	int count = ntokens - 1;
1030	pp->search_list[cat].count = count;
1031	if (count == 0)
1032	{
1033		return;
1034	}
1035
1036	pp->search_list[cat].module = (si_mod_t **)calloc(pp->search_list[cat].count, sizeof(si_mod_t *));
1037	pp->search_list[cat].module_flags = (uint32_t *)calloc(pp->search_list[cat].count, sizeof(uint32_t));
1038	if ((pp->search_list[cat].module == NULL) || (pp->search_list[cat].module_flags == NULL))
1039	{
1040		free(pp->search_list[cat].module);
1041		free(pp->search_list[cat].module_flags);
1042		return;
1043	}
1044
1045	int i, j;
1046	for (i = 1, j = 0; i < ntokens; i++)
1047	{
1048		si_mod_t *mod = si_module_with_name(tokens[i]);
1049		if (mod != NULL)
1050		{
1051			pp->search_list[cat].module[j] = mod;
1052			j++;
1053
1054			if (string_equal(tokens[i], "cache"))
1055			{
1056				pp->search_list[cat].flags |= SEARCH_FLAG_CACHE_ENABLED;
1057			}
1058		}
1059	}
1060}
1061