Libinfo-517.tar.gz
[apple/libinfo.git] / lookup.subproj / si_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 <sys/types.h>
25 #include <sys/socket.h>
26 #include <net/ethernet.h>
27 #include <libkern/OSAtomic.h>
28 #include <dlfcn.h>
29 #include <string.h>
30 #include <stdio.h>
31 #include <errno.h>
32 #include <pthread.h>
33 #include <mach/mach.h>
34 #include <dispatch/dispatch.h>
35 #include <dispatch/private.h>
36 #include "si_module.h"
37
38 #define PLUGIN_DIR_PATH "/usr/lib/info"
39 #define PLUGIN_BUNDLE_SUFFIX "so"
40
41 #define WORKUNIT_CANCELLED 0x00000001
42 #define WORKUNIT_RETURNS_LIST 0x00000002
43
44 #ifdef __BIG_ENDIAN__
45 #define WORKUNIT_CANCELLED_BIT_ADDRESS 31
46 #else
47 #define WORKUNIT_CANCELLED_BIT_ADDRESS 7
48 #endif
49
50 typedef struct si_async_workunit_s
51 {
52 si_mod_t *si;
53 uint32_t call;
54 char *str1;
55 char *str2;
56 char *str3;
57 uint32_t num1;
58 uint32_t num2;
59 uint32_t num3;
60 uint32_t num4;
61 uint32_t err;
62 /* async support below */
63 uint32_t flags;
64 int32_t refcount;
65 void *callback;
66 void *context;
67 mach_port_t port;
68 mach_port_t send;
69 si_item_t *resitem;
70 si_list_t *reslist;
71 struct si_async_workunit_s *next;
72 } si_async_workunit_t;
73
74 static si_mod_t **module_list = NULL;
75 static uint32_t module_count = 0;
76 static pthread_mutex_t module_mutex = PTHREAD_MUTEX_INITIALIZER;
77 static si_async_workunit_t *si_async_worklist = NULL;
78
79 si_mod_t *si_module_static_search(void);
80 si_mod_t *si_module_static_cache(void);
81 si_mod_t *si_module_static_file(void);
82 #ifdef MUSER_AVAILABLE
83 si_mod_t *si_module_static_muser(void);
84 #endif
85 #ifdef DS_AVAILABLE
86 si_mod_t *si_module_static_ds(void);
87 #endif
88 si_mod_t *si_module_static_mdns(void);
89
90 static void *
91 si_mod_dlsym(void *so, const char *name, const char *sym)
92 {
93 char *str;
94 void *out;
95
96 if ((so == NULL) || (name == NULL) || (sym == NULL)) return NULL;
97
98 str = NULL;
99 asprintf(&str, "%s_%s", name, sym);
100 if (str == NULL) return NULL;
101
102 out = dlsym(so, str);
103 free(str);
104 return out;
105 }
106
107 si_mod_t *
108 si_module_with_path(const char *path, const char *name)
109 {
110 void *so;
111 int (*si_sym_init)(si_mod_t *);
112 void (*si_sym_close)(si_mod_t *);
113 int status;
114 si_mod_t *out;
115 char *outname;
116
117 if ((path == NULL) || (name == NULL))
118 {
119 errno = EINVAL;
120 return NULL;
121 }
122
123 so = dlopen(path, RTLD_LOCAL);
124 if (so == NULL) return NULL;
125
126 si_sym_init = si_mod_dlsym(so, name, "init");
127 if (si_sym_init == NULL)
128 {
129 dlclose(so);
130 errno = ECONNREFUSED;
131 return NULL;
132 }
133
134 si_sym_close = si_mod_dlsym(so, name, "close");
135 if (si_sym_close == NULL)
136 {
137 dlclose(so);
138 errno = ECONNREFUSED;
139 return NULL;
140 }
141
142 out = (si_mod_t *)calloc(1, sizeof(si_mod_t));
143 outname = strdup(name);
144
145 if ((out == NULL) || (outname == NULL))
146 {
147 free(out);
148 free(outname);
149 dlclose(so);
150 errno = ENOMEM;
151 return NULL;
152 }
153
154 out->name = outname;
155 out->refcount = 1;
156 out->flags = 0;
157 out->bundle = so;
158
159 status = si_sym_init(out);
160 if (status != 0)
161 {
162 dlclose(so);
163 free(out);
164 free(outname);
165 errno = ECONNREFUSED;
166 return NULL;
167 }
168
169 return out;
170 }
171
172 si_mod_t *
173 si_module_with_name(const char *name)
174 {
175 static struct
176 {
177 const char *name;
178 si_mod_t *(*init)(void);
179 si_mod_t *module;
180 } modules[] =
181 {
182 { "search", si_module_static_search, NULL },
183 { "cache", si_module_static_cache, NULL },
184 { "file", si_module_static_file, NULL },
185 #ifdef MUSER_AVAILABLE
186 { "muser", si_module_static_muser, NULL },
187 #endif
188 #ifdef DS_AVAILABLE
189 { "ds", si_module_static_ds, NULL },
190 #endif
191 { "mdns", si_module_static_mdns, NULL },
192 { NULL, NULL },
193 };
194
195 si_mod_t *si = NULL;
196 int i;
197
198 /*
199 * We don't need to worry about locking during initialization
200 * because all modules init routine returns static storage.
201 */
202
203 /* Find the module by name */
204 for (i = 0; modules[i].name != NULL; ++i)
205 {
206 if (string_equal(name, modules[i].name))
207 {
208 si = modules[i].module;
209 if (si == NULL)
210 {
211 si = modules[i].init();
212 modules[i].module = si;
213 }
214
215 break;
216 }
217 }
218
219 if (si != NULL) return si;
220
221 pthread_mutex_lock(&module_mutex);
222 char *path = NULL;
223
224 asprintf(&path, "%s/%s.%s", PLUGIN_DIR_PATH, name, PLUGIN_BUNDLE_SUFFIX);
225
226 if (path == NULL)
227 {
228 errno = ENOMEM;
229 pthread_mutex_unlock(&module_mutex);
230 return NULL;
231 }
232
233 si = si_module_with_path(path, name);
234 free(path);
235
236 if (si == NULL)
237 {
238 pthread_mutex_unlock(&module_mutex);
239 return NULL;
240 }
241
242 /* add out to module_list */
243 module_list = (si_mod_t **)reallocf(module_list, (module_count + 1) * sizeof(si_mod_t *));
244 if (module_list == NULL)
245 {
246 pthread_mutex_unlock(&module_mutex);
247 return si;
248 }
249
250 module_list[module_count] = si;
251 module_count++;
252
253 pthread_mutex_unlock(&module_mutex);
254
255 return si;
256 }
257
258 si_mod_t *
259 si_module_retain(si_mod_t *si)
260 {
261 if (si == NULL) return NULL;
262 if (si->flags & SI_MOD_FLAG_STATIC) return si;
263
264 OSAtomicIncrement32Barrier(&si->refcount);
265
266 return si;
267 }
268
269 void
270 si_module_release(si_mod_t *si)
271 {
272 int32_t i;
273
274 if (si == NULL) return;
275 if (si->flags & SI_MOD_FLAG_STATIC) return;
276
277 i = OSAtomicDecrement32Barrier(&si->refcount);
278 if (i > 0) return;
279
280 pthread_mutex_lock(&module_mutex);
281
282 for (i = 0; (i < module_count) && (module_list[i] != si); i++);
283 if (i >= module_count)
284 {
285 pthread_mutex_unlock(&module_mutex);
286 return;
287 }
288
289 if (module_count == 1)
290 {
291 free(module_list);
292 module_list = NULL;
293 module_count = 0;
294 pthread_mutex_unlock(&module_mutex);
295 return;
296 }
297
298 for (i++; i < module_count; i++) module_list[i - 1] = module_list[i];
299 module_count--;
300 module_list = (si_mod_t **)reallocf(module_list, module_count * sizeof(si_mod_t *));
301 if (module_list == NULL) module_count = 0;
302
303 pthread_mutex_unlock(&module_mutex);
304
305 if (si->vtable->sim_close != NULL) si->vtable->sim_close(si);
306 if (si->bundle != NULL) dlclose(si->bundle);
307 free(si->name);
308 free(si);
309 }
310
311 const char *
312 si_module_name(si_mod_t *si)
313 {
314 if (si == NULL) return NULL;
315
316 return (const char *)si->name;
317 }
318
319 int
320 si_module_vers(si_mod_t *si)
321 {
322 if (si == NULL) return 0;
323
324 return si->vers;
325 }
326
327 int
328 si_item_match(si_item_t *item, int cat, const void *name, uint32_t num, int which)
329 {
330 int i;
331 union
332 {
333 char *x;
334 struct passwd *u;
335 struct group *g;
336 struct grouplist_s *l;
337 struct aliasent *a;
338 struct hostent *h;
339 struct netent *n;
340 struct servent *s;
341 struct protoent *p;
342 struct rpcent *r;
343 struct fstab *f;
344 struct mac_s *m;
345 } ent;
346
347 if (item == NULL) return 0;
348 if (which == SEL_ALL) return 1;
349 if ((which == SEL_NAME) && (name == NULL)) return 0;
350
351 ent.x = (char *)((uintptr_t)item + sizeof(si_item_t));
352
353 switch (cat)
354 {
355 case CATEGORY_USER:
356 {
357 if ((which == SEL_NAME) && (string_equal(name, ent.u->pw_name))) return 1;
358 else if ((which == SEL_NUMBER) && (num == (uint32_t)ent.u->pw_uid)) return 1;
359 return 0;
360 }
361 case CATEGORY_GROUP:
362 {
363 if ((which == SEL_NAME) && (string_equal(name, ent.g->gr_name))) return 1;
364 else if ((which == SEL_NUMBER) && (num == (uint32_t)ent.g->gr_gid)) return 1;
365 return 0;
366 }
367 case CATEGORY_GROUPLIST:
368 {
369 if ((which == SEL_NAME) && (string_equal(name, ent.l->gl_user))) return 1;
370 return 0;
371 }
372 case CATEGORY_ALIAS:
373 {
374 if ((which == SEL_NAME) && (string_equal(name, ent.a->alias_name))) return 1;
375 return 0;
376 }
377 case CATEGORY_HOST_IPV4:
378 case CATEGORY_HOST_IPV6:
379 {
380 /* N.B. address family is passed in num variable */
381 if (ent.h->h_addrtype != num) return 0;
382 if (which == SEL_NAME)
383 {
384 if (string_equal(name, ent.h->h_name)) return 1;
385 if (ent.h->h_aliases != NULL)
386 {
387 for (i = 0; ent.h->h_aliases[i] != NULL; i++)
388 {
389 if (string_equal(name, ent.h->h_aliases[i])) return 1;
390 }
391 }
392 }
393 else if (which == SEL_NUMBER)
394 {
395 if (memcmp(name, ent.h->h_addr_list[0], ent.h->h_length) == 0) return 1;
396 }
397 return 0;
398 }
399 case CATEGORY_NETWORK:
400 {
401 if (which == SEL_NAME)
402 {
403 if (string_equal(name, ent.n->n_name)) return 1;
404 if (ent.n->n_aliases != NULL)
405 {
406 for (i = 0; ent.n->n_aliases[i] != NULL; i++)
407 {
408 if (string_equal(name, ent.n->n_aliases[i])) return 1;
409 }
410 }
411 }
412 else if (which == SEL_NUMBER)
413 {
414 if (num == ent.n->n_net) return 1;
415 }
416 return 0;
417 }
418 case CATEGORY_SERVICE:
419 {
420 if (which == SEL_NAME)
421 {
422 /* N.B. for SEL_NAME, num is 0 (don't care), 1 (udp) or 2 (tcp) */
423 if ((num == 1) && (string_not_equal("udp", ent.s->s_proto))) return 0;
424 if ((num == 2) && (string_not_equal("tcp", ent.s->s_proto))) return 0;
425
426 if (string_equal(name, ent.s->s_name)) return 1;
427 if (ent.s->s_aliases != NULL)
428 {
429 for (i = 0; ent.s->s_aliases[i] != NULL; i++)
430 {
431 if (string_equal(name, ent.s->s_aliases[i])) return 1;
432 }
433 }
434 }
435 else if (which == SEL_NUMBER)
436 {
437 /* N.B. for SEL_NUMBER, protocol is sent in name variable */
438 if ((name != NULL) && (string_not_equal(name, ent.s->s_proto))) return 0;
439 if (num == ent.s->s_port) return 1;
440 }
441 return 0;
442 }
443 case CATEGORY_PROTOCOL:
444 {
445 if (which == SEL_NAME)
446 {
447 if (string_equal(name, ent.p->p_name)) return 1;
448 if (ent.p->p_aliases != NULL)
449 {
450 for (i = 0; ent.p->p_aliases[i] != NULL; i++)
451 {
452 if (string_equal(name, ent.p->p_aliases[i])) return 1;
453 }
454 }
455 }
456 else if (which == SEL_NUMBER)
457 {
458 if (num == ent.p->p_proto) return 1;
459 }
460 return 0;
461 }
462 case CATEGORY_RPC:
463 {
464 if (which == SEL_NAME)
465 {
466 if (string_equal(name, ent.r->r_name)) return 1;
467 if (ent.r->r_aliases != NULL)
468 {
469 for (i = 0; ent.r->r_aliases[i] != NULL; i++)
470 {
471 if (string_equal(name, ent.r->r_aliases[i])) return 1;
472 }
473 }
474 }
475 else if (which == SEL_NUMBER)
476 {
477 if (num == ent.r->r_number) return 1;
478 }
479 return 0;
480 }
481 case CATEGORY_FS:
482 {
483 if ((which == SEL_NAME) && (string_equal(name, ent.f->fs_spec))) return 1;
484 if ((which == SEL_NUMBER) && (string_equal(name, ent.f->fs_file))) return 1;
485 return 0;
486 }
487 case CATEGORY_MAC:
488 {
489 if ((which == SEL_NAME) && (string_equal(name, ent.m->host))) return 1;
490 if ((which == SEL_NUMBER) && (string_equal(name, ent.m->mac))) return 1;
491 return 0;
492 }
493 default: return 0;
494 }
495
496 return 0;
497 }
498
499 int
500 si_item_is_valid(si_item_t *item)
501 {
502 si_mod_t *si;
503
504 if (item == NULL) return 0;
505
506 si = item->src;
507
508 if (si == NULL) return 0;
509 if (si->vtable->sim_is_valid == NULL) return 0;
510
511 return si->vtable->sim_is_valid(si, item);
512 }
513
514 si_item_t *
515 si_user_byname(si_mod_t *si, const char *name)
516 {
517 if (si == NULL) return NULL;
518 if (si->vtable->sim_user_byname == NULL) return NULL;
519 return si->vtable->sim_user_byname(si, name);
520 }
521
522 si_item_t *
523 si_user_byuid(si_mod_t *si, uid_t uid)
524 {
525 if (si == NULL) return NULL;
526 if (si->vtable->sim_user_byuid == NULL) return NULL;
527 return si->vtable->sim_user_byuid(si, uid);
528 }
529
530 si_item_t *
531 si_user_byuuid(si_mod_t *si, uuid_t uuid)
532 {
533 if (si == NULL) return NULL;
534 if (si->vtable->sim_user_byuuid == NULL) return NULL;
535 return si->vtable->sim_user_byuuid(si, uuid);
536 }
537
538 si_list_t *
539 si_user_all(si_mod_t *si)
540 {
541 if (si == NULL) return NULL;
542 if (si->vtable->sim_user_all == NULL) return NULL;
543 return si->vtable->sim_user_all(si);
544 }
545
546 si_item_t *
547 si_group_byname(si_mod_t *si, const char *name)
548 {
549 if (si == NULL) return NULL;
550 if (si->vtable->sim_group_byname == NULL) return NULL;
551 return si->vtable->sim_group_byname(si, name);
552 }
553
554 si_item_t *
555 si_group_bygid(si_mod_t *si, gid_t gid)
556 {
557 if (si == NULL) return NULL;
558 if (si->vtable->sim_group_bygid == NULL) return NULL;
559 return si->vtable->sim_group_bygid(si, gid);
560 }
561
562 si_item_t *
563 si_group_byuuid(si_mod_t *si, uuid_t uuid)
564 {
565 if (si == NULL) return NULL;
566 if (si->vtable->sim_group_byuuid == NULL) return NULL;
567 return si->vtable->sim_group_byuuid(si, uuid);
568 }
569
570 si_list_t *
571 si_group_all(si_mod_t *si)
572 {
573 if (si == NULL) return NULL;
574 if (si->vtable->sim_group_all == NULL) return NULL;
575 return si->vtable->sim_group_all(si);
576 }
577
578 si_item_t *
579 si_grouplist(si_mod_t *si, const char *name, uint32_t count)
580 {
581 if (si == NULL) return NULL;
582 if (si->vtable->sim_grouplist == NULL) return NULL;
583 return si->vtable->sim_grouplist(si, name, count);
584 }
585
586 si_list_t *
587 si_netgroup_byname(struct si_mod_s *si, const char *name)
588 {
589 if (si == NULL) return NULL;
590 if (si->vtable->sim_netgroup_byname == NULL) return NULL;
591 return si->vtable->sim_netgroup_byname(si, name);
592 }
593
594 int
595 si_in_netgroup(struct si_mod_s *si, const char *name, const char *host, const char *user, const char *domain)
596 {
597 if (si == NULL) return 0;
598 if (si->vtable->sim_in_netgroup == NULL) return 0;
599 return si->vtable->sim_in_netgroup(si, name, host, user, domain);
600 }
601
602 si_item_t *
603 si_alias_byname(si_mod_t *si, const char *name)
604 {
605 if (si == NULL) return NULL;
606 if (si->vtable->sim_alias_byname == NULL) return NULL;
607 return si->vtable->sim_alias_byname(si, name);
608 }
609
610 si_list_t *
611 si_alias_all(si_mod_t *si)
612 {
613 if (si == NULL) return NULL;
614 if (si->vtable->sim_alias_all == NULL) return NULL;
615 return si->vtable->sim_alias_all(si);
616 }
617
618 si_item_t *
619 si_host_byname(si_mod_t *si, const char *name, int af, const char *interface, uint32_t *err)
620 {
621 if (si == NULL) return NULL;
622 if (si->vtable->sim_host_byname == NULL) return NULL;
623 return si->vtable->sim_host_byname(si, name, af, interface, err);
624 }
625
626 si_item_t *
627 si_host_byaddr(si_mod_t *si, const void *addr, int af, const char *interface, uint32_t *err)
628 {
629 if (si == NULL) return NULL;
630 if (si->vtable->sim_host_byaddr == NULL) return NULL;
631 return si->vtable->sim_host_byaddr(si, addr, af, interface, err);
632 }
633
634 si_list_t *
635 si_host_all(si_mod_t *si)
636 {
637 if (si == NULL) return NULL;
638 if (si->vtable->sim_host_all == NULL) return NULL;
639 return si->vtable->sim_host_all(si);
640 }
641
642 si_item_t *
643 si_mac_byname(struct si_mod_s *si, const char *name)
644 {
645 if (si == NULL) return NULL;
646 if (si->vtable->sim_mac_byname == NULL) return NULL;
647 return si->vtable->sim_mac_byname(si, name);
648 }
649
650 si_item_t *
651 si_mac_bymac(struct si_mod_s *si, const char *mac)
652 {
653 if (si == NULL) return NULL;
654 if (si->vtable->sim_mac_bymac == NULL) return NULL;
655 return si->vtable->sim_mac_bymac(si, mac);
656 }
657
658 si_list_t *
659 si_mac_all(si_mod_t *si)
660 {
661 if (si == NULL) return NULL;
662 if (si->vtable->sim_mac_all == NULL) return NULL;
663 return si->vtable->sim_mac_all(si);
664 }
665
666 si_item_t *
667 si_network_byname(si_mod_t *si, const char *name)
668 {
669 if (si == NULL) return NULL;
670 if (si->vtable->sim_network_byname == NULL) return NULL;
671 return si->vtable->sim_network_byname(si, name);
672 }
673
674 si_item_t *
675 si_network_byaddr(si_mod_t *si, uint32_t addr)
676 {
677 if (si == NULL) return NULL;
678 if (si->vtable->sim_network_byaddr == NULL) return NULL;
679 return si->vtable->sim_network_byaddr(si, addr);
680 }
681
682 si_list_t *
683 si_network_all(si_mod_t *si)
684 {
685 if (si == NULL) return NULL;
686 if (si->vtable->sim_network_all == NULL) return NULL;
687 return si->vtable->sim_network_all(si);
688 }
689
690 si_item_t *
691 si_service_byname(si_mod_t *si, const char *name, const char *proto)
692 {
693 if (si == NULL) return NULL;
694 if (si->vtable->sim_service_byname == NULL) return NULL;
695 return si->vtable->sim_service_byname(si, name, proto);
696 }
697
698 si_item_t *
699 si_service_byport(si_mod_t *si, int port, const char *proto)
700 {
701 if (si == NULL) return NULL;
702 if (si->vtable->sim_service_byport == NULL) return NULL;
703 return si->vtable->sim_service_byport(si, port, proto);
704 }
705
706 si_list_t *
707 si_service_all(si_mod_t *si)
708 {
709 if (si == NULL) return NULL;
710 if (si->vtable->sim_service_all == NULL) return NULL;
711 return si->vtable->sim_service_all(si);
712 }
713
714 si_item_t *
715 si_protocol_byname(si_mod_t *si, const char *name)
716 {
717 if (si == NULL) return NULL;
718 if (si->vtable->sim_protocol_byname == NULL) return NULL;
719 return si->vtable->sim_protocol_byname(si, name);
720 }
721
722 si_item_t *
723 si_protocol_bynumber(si_mod_t *si, uint32_t number)
724 {
725 if (si == NULL) return NULL;
726 if (si->vtable->sim_protocol_bynumber == NULL) return NULL;
727 return si->vtable->sim_protocol_bynumber(si, number);
728 }
729
730 si_list_t *
731 si_protocol_all(si_mod_t *si)
732 {
733 if (si == NULL) return NULL;
734 if (si->vtable->sim_protocol_all == NULL) return NULL;
735 return si->vtable->sim_protocol_all(si);
736 }
737
738 si_item_t *
739 si_rpc_byname(si_mod_t *si, const char *name)
740 {
741 if (si == NULL) return NULL;
742 if (si->vtable->sim_rpc_byname == NULL) return NULL;
743 return si->vtable->sim_rpc_byname(si, name);
744 }
745
746 si_item_t *
747 si_rpc_bynumber(si_mod_t *si, int number)
748 {
749 if (si == NULL) return NULL;
750 if (si->vtable->sim_rpc_bynumber == NULL) return NULL;
751 return si->vtable->sim_rpc_bynumber(si, number);
752 }
753
754 si_list_t *
755 si_rpc_all(si_mod_t *si)
756 {
757 if (si == NULL) return NULL;
758 if (si->vtable->sim_rpc_all == NULL) return NULL;
759 return si->vtable->sim_rpc_all(si);
760 }
761
762 si_item_t *
763 si_fs_byspec(si_mod_t *si, const char *spec)
764 {
765 if (si == NULL) return NULL;
766 if (si->vtable->sim_fs_byspec == NULL) return NULL;
767 return si->vtable->sim_fs_byspec(si, spec);
768 }
769
770 si_item_t *
771 si_fs_byfile(si_mod_t *si, const char *file)
772 {
773 if (si == NULL) return NULL;
774 if (si->vtable->sim_fs_byfile == NULL) return NULL;
775 return si->vtable->sim_fs_byfile(si, file);
776 }
777
778 si_list_t *
779 si_fs_all(si_mod_t *si)
780 {
781 if (si == NULL) return NULL;
782 if (si->vtable->sim_fs_all == NULL) return NULL;
783 return si->vtable->sim_fs_all(si);
784 }
785
786 si_item_t *
787 si_item_call(struct si_mod_s *si, int call, const char *str1, const char *str2, const char *str3, uint32_t num1, uint32_t num2, uint32_t *err)
788 {
789 if (si == NULL) return NULL;
790
791 switch (call)
792 {
793 case SI_CALL_USER_BYNAME: return si_user_byname(si, str1);
794 case SI_CALL_USER_BYUID: return si_user_byuid(si, (uid_t)num1);
795 case SI_CALL_GROUP_BYNAME: return si_group_byname(si, str1);
796 case SI_CALL_GROUP_BYGID: return si_group_bygid(si, (gid_t)num1);
797 case SI_CALL_GROUPLIST: return si_grouplist(si, str1, (int) num1);
798 case SI_CALL_ALIAS_BYNAME: return si_alias_byname(si, str1);
799 case SI_CALL_HOST_BYNAME: return si_host_byname(si, str1, num1, str3, err);
800 case SI_CALL_HOST_BYADDR: return si_host_byaddr(si, (void *)str1, num1, str3, err);
801 case SI_CALL_NETWORK_BYNAME: return si_network_byname(si, str1);
802 case SI_CALL_NETWORK_BYADDR: return si_network_byaddr(si, num1);
803 case SI_CALL_SERVICE_BYNAME: return si_service_byname(si, str1, str2);
804 case SI_CALL_SERVICE_BYPORT: return si_service_byport(si, num1, str2);
805 case SI_CALL_PROTOCOL_BYNAME: return si_protocol_byname(si, str1);
806 case SI_CALL_PROTOCOL_BYNUMBER: return si_protocol_bynumber(si, num1);
807 case SI_CALL_RPC_BYNAME: return si_network_byname(si, str1);
808 case SI_CALL_RPC_BYNUMBER: return si_rpc_bynumber(si, num1);
809 case SI_CALL_FS_BYSPEC: return si_fs_byspec(si, str1);
810 case SI_CALL_FS_BYFILE: return si_fs_byfile(si, str1);
811 case SI_CALL_NAMEINFO: return si_nameinfo(si, (const struct sockaddr *)str1, num1, str3, err);
812 case SI_CALL_IPNODE_BYNAME: return si_ipnode_byname(si, (const char *)str1, num1, num2, str3, err);
813 case SI_CALL_MAC_BYNAME: return si_mac_byname(si, (const char *)str1);
814 case SI_CALL_MAC_BYMAC: return si_mac_bymac(si, (const char *)str1);
815
816 /* Support for DNS async calls */
817 case SI_CALL_DNS_QUERY:
818 case SI_CALL_DNS_SEARCH:
819 {
820 if (si->vtable->sim_item_call == NULL) return NULL;
821 return si->vtable->sim_item_call(si, call, str1, str2, str3, num1, num2, err);
822 }
823
824 default: return NULL;
825 }
826
827 return NULL;
828 }
829
830 si_list_t *
831 si_list_call(struct si_mod_s *si, int call, const char *str1, const char *str2, const char *str3, uint32_t num1, uint32_t num2, uint32_t num3, uint32_t num4, uint32_t *err)
832 {
833 if (si == NULL) return NULL;
834
835 switch (call)
836 {
837 case SI_CALL_USER_ALL: return si_user_all(si);
838 case SI_CALL_GROUP_ALL: return si_group_all(si);
839 case SI_CALL_ALIAS_ALL: return si_alias_all(si);
840 case SI_CALL_HOST_ALL: return si_host_all(si);
841 case SI_CALL_NETWORK_ALL: return si_network_all(si);
842 case SI_CALL_SERVICE_ALL: return si_service_all(si);
843 case SI_CALL_PROTOCOL_ALL: return si_protocol_all(si);
844 case SI_CALL_RPC_ALL: return si_rpc_all(si);
845 case SI_CALL_FS_ALL: return si_fs_all(si);
846 case SI_CALL_MAC_ALL: return si_mac_all(si);
847 case SI_CALL_ADDRINFO: return si_addrinfo(si, str1, str2, num1, num2, num3, num4, str3, err);
848 default: return NULL;
849 }
850
851 return NULL;
852 }
853
854 static void
855 si_async_worklist_add_unit(si_async_workunit_t *r)
856 {
857 pthread_mutex_lock(&module_mutex);
858 r->next = si_async_worklist;
859 si_async_worklist = r;
860 pthread_mutex_unlock(&module_mutex);
861 }
862
863 static void
864 si_async_worklist_remove_unit(si_async_workunit_t *r)
865 {
866 si_async_workunit_t *x;
867
868 pthread_mutex_lock(&module_mutex);
869 if (si_async_worklist == r)
870 {
871 si_async_worklist = r->next;
872 }
873 else
874 {
875 for (x = si_async_worklist; (x != NULL) && (x->next != r); x = x->next) {;}
876 if (x != NULL) x->next = r->next;
877 }
878 pthread_mutex_unlock(&module_mutex);
879 }
880
881 static si_async_workunit_t*
882 si_async_worklist_find_unit(mach_port_t p)
883 {
884 si_async_workunit_t *r;
885
886 pthread_mutex_lock(&module_mutex);
887 for (r = si_async_worklist; (r != NULL) && (r->port != p); r = r->next) {;}
888 pthread_mutex_unlock(&module_mutex);
889
890 return r;
891 }
892
893 static si_async_workunit_t *
894 si_async_workunit_create(si_mod_t *si, int call, const char *str1, const char *str2, const char *str3, uint32_t num1, uint32_t num2, uint32_t num3, uint32_t num4, void *callback, void *context)
895 {
896 si_async_workunit_t *r;
897 kern_return_t status;
898 mach_port_t reply, send;
899 mach_msg_type_name_t type;
900 char *s1, *s2, *s3;
901
902 s1 = NULL;
903 s2 = NULL;
904 s3 = NULL;
905
906 if (si_call_str1_is_buffer(call))
907 {
908 if (num3 > 0)
909 {
910 s1 = calloc(1, num3);
911 if (s1 == NULL) return NULL;
912 memcpy(s1, str1, num3);
913 }
914 }
915 else if (str1 != NULL)
916 {
917 s1 = strdup(str1);
918 if (s1 == NULL) return NULL;
919 }
920
921 if (str2 != NULL)
922 {
923 s2 = strdup(str2);
924 if (s2 == NULL)
925 {
926 if (s1 != NULL) free(s1);
927 return NULL;
928 }
929 }
930
931 if (str3 != NULL)
932 {
933 s3 = strdup(str3);
934 if (s3 == NULL)
935 {
936 if (s1 != NULL) free(s1);
937 if (s2 != NULL) free(s2);
938 return NULL;
939 }
940 }
941
942 r = (si_async_workunit_t *)calloc(1, sizeof(si_async_workunit_t));
943 if (r == NULL)
944 {
945 if (s1 != NULL) free(s1);
946 if (s2 != NULL) free(s2);
947 if (s3 != NULL) free(s3);
948 return NULL;
949 }
950
951 reply = MACH_PORT_NULL;
952 send = MACH_PORT_NULL;
953 type = 0;
954
955 status = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &reply);
956 if (status == KERN_SUCCESS) status = mach_port_extract_right(mach_task_self(), reply, MACH_MSG_TYPE_MAKE_SEND_ONCE, &send, &type);
957 if (status != KERN_SUCCESS)
958 {
959 if (reply != MACH_PORT_NULL) mach_port_mod_refs(mach_task_self(), reply, MACH_PORT_RIGHT_RECEIVE, -1);
960 if (s1 != NULL) free(s1);
961 if (s2 != NULL) free(s2);
962 if (s3 != NULL) free(s3);
963 free(r);
964 return NULL;
965 }
966
967 r->si = si;
968 r->call = call;
969 r->str1 = s1;
970 r->str2 = s2;
971 r->str3 = s3;
972 r->num1 = num1;
973 r->num2 = num2;
974 r->num3 = num3;
975 r->num4 = num4;
976
977 r->refcount = 2;
978 r->flags = 0;
979 if (si_call_returns_list(call)) r->flags |= WORKUNIT_RETURNS_LIST;
980
981 r->callback = callback;
982 r->context = context;
983 r->port = reply;
984 r->send = send;
985
986 si_async_worklist_add_unit(r);
987
988 return r;
989 }
990
991 static void
992 si_async_workunit_release(si_async_workunit_t *r)
993 {
994 if (r == NULL) return;
995
996 if (OSAtomicDecrement32Barrier(&(r->refcount)) != 0) return;
997
998 #ifdef CALL_TRACE
999 fprintf(stderr, "** %s freeing worklist item %p\n", __func__, r);
1000 #endif
1001
1002 si_async_worklist_remove_unit(r);
1003
1004 if (r->resitem != NULL) si_item_release(r->resitem);
1005 if (r->reslist != NULL) si_list_release(r->reslist);
1006
1007 if (r->str1 != NULL) free(r->str1);
1008 if (r->str2 != NULL) free(r->str2);
1009 if (r->str3 != NULL) free(r->str3);
1010
1011 /* release send-once right if it has not been used */
1012 if (r->send != MACH_PORT_NULL) mach_port_deallocate(mach_task_self(), r->send);
1013
1014 /* release receive right */
1015 mach_port_mod_refs(mach_task_self(), r->port, MACH_PORT_RIGHT_RECEIVE, -1);
1016
1017 free(r);
1018 }
1019
1020 static void
1021 si_async_launchpad(si_async_workunit_t *r)
1022 {
1023 kern_return_t status;
1024 mach_msg_empty_send_t msg;
1025
1026 #ifdef CALL_TRACE
1027 fprintf(stderr, "** %s starting worklist item %p\n", __func__, r);
1028 #endif
1029
1030 if (r->flags & WORKUNIT_CANCELLED)
1031 {
1032 si_async_workunit_release(r);
1033 #ifdef CALL_TRACE
1034 fprintf(stderr, "** %s worklist item %p was cancelled early\n", __func__, r);
1035 #endif
1036 return;
1037 }
1038
1039 if (r->flags & WORKUNIT_RETURNS_LIST) r->reslist = si_list_call(r->si, r->call, r->str1, r->str2, r->str3, r->num1, r->num2, r->num3, r->num4, &(r->err));
1040 else r->resitem = si_item_call(r->si, r->call, r->str1, r->str2, r->str3, r->num1, r->num2, &(r->err));
1041
1042 /*
1043 * Test and set the cancelled flag.
1044 * If it was set, then this work item was cancelled.
1045 * Otherwise, setting it here prevents si_async_cancel from cancelling:
1046 * too late to cancel now!
1047 */
1048 if (OSAtomicTestAndSetBarrier(WORKUNIT_CANCELLED_BIT_ADDRESS, &(r->flags)) == 1)
1049 {
1050 si_async_workunit_release(r);
1051 #ifdef CALL_TRACE
1052 fprintf(stderr, "** %s worklist item %p was cancelled in flight\n", __func__, r);
1053 #endif
1054 return;
1055 }
1056 #ifdef CALL_TRACE
1057 else fprintf(stderr, "** %s worklist item %p flags are now 0x%08x\n", __func__, r, r->flags);
1058 #endif
1059
1060 memset(&msg, 0, sizeof(mach_msg_empty_send_t));
1061
1062 msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE, MACH_MSGH_BITS_ZERO);
1063 msg.header.msgh_remote_port = r->send;
1064 r->send = MACH_PORT_NULL;
1065 msg.header.msgh_local_port = MACH_PORT_NULL;
1066 msg.header.msgh_size = sizeof(mach_msg_empty_send_t);
1067 msg.header.msgh_id = r->call;
1068
1069 status = mach_msg(&(msg.header), MACH_SEND_MSG, msg.header.msgh_size, 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
1070 if (status != MACH_MSG_SUCCESS)
1071 {
1072 /* receiver failed - clean up to avoid a port leak */
1073 mach_msg_destroy(&(msg.header));
1074 #ifdef CALL_TRACE
1075 fprintf(stderr, "** %s mach message send failed for worklist item %p\n", __func__, r);
1076 #endif
1077 }
1078
1079 si_async_workunit_release(r);
1080
1081 /*
1082 * The client is now responsible for calling si_async_handle_reply,
1083 * which will invoke the client's callback and then release the workunit.
1084 */
1085
1086 #ifdef CALL_TRACE
1087 fprintf(stderr, "** %s completed async worklist item %p\n", __func__, r);
1088 #endif
1089 }
1090
1091 mach_port_t
1092 si_async_call(struct si_mod_s *si, int call, const char *str1, const char *str2, const char *str3, uint32_t num1, uint32_t num2, uint32_t num3, uint32_t num4, void *callback, void *context)
1093 {
1094 si_async_workunit_t *req;
1095
1096 if (si == NULL) return MACH_PORT_NULL;
1097 if (callback == NULL) return MACH_PORT_NULL;
1098
1099 /* if module does async on it's own, hand off the call */
1100 if (si->vtable->sim_async_call != NULL)
1101 {
1102 return si->vtable->sim_async_call(si, call, str1, str2, str3, num1, num2, num3, num4, callback, context);
1103 }
1104
1105 req = si_async_workunit_create(si, call, str1, str2, str3, num1, num2, num3, num4, callback, context);
1106 if (req == NULL) return MACH_PORT_NULL;
1107
1108 /* queue the work on the global low-priority dispatch queue */
1109 #ifdef CALL_TRACE
1110 fprintf(stderr, "** %s dispatching worklist item %p\n", __func__, req);
1111 #endif
1112 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, DISPATCH_QUEUE_OVERCOMMIT), ^{ si_async_launchpad(req); });
1113
1114 return req->port;
1115 }
1116
1117 void
1118 si_async_cancel(mach_port_t p)
1119 {
1120 si_async_workunit_t *r;
1121
1122 r = si_async_worklist_find_unit(p);
1123 if (r == NULL)
1124 {
1125 #ifdef CALL_TRACE
1126 fprintf(stderr, "** %s can't find worklist item\n", __func__);
1127 #endif
1128 return;
1129 }
1130
1131 /*
1132 * Test and set the WORKUNIT_CANCELLED flag.
1133 * If it was already set, this work item has been executed - too late to really cancel.
1134 */
1135 if (OSAtomicTestAndSetBarrier(WORKUNIT_CANCELLED_BIT_ADDRESS, &(r->flags)) == 1)
1136 {
1137 /* already executed */
1138 #ifdef CALL_TRACE
1139 fprintf(stderr, "** %s worklist item %p has executed\n", __func__, r);
1140 #endif
1141 }
1142
1143 #ifdef CALL_TRACE
1144 fprintf(stderr, "** %s calling worklist item %p callback SI_STATUS_CALL_CANCELLED\n", __func__, r);
1145 #endif
1146
1147 if (r->callback != NULL)
1148 {
1149 if (r->flags & WORKUNIT_RETURNS_LIST) ((list_async_callback)(r->callback))(NULL, SI_STATUS_CALL_CANCELLED, r->context);
1150 else ((item_async_callback)(r->callback))(NULL, SI_STATUS_CALL_CANCELLED, r->context);
1151 }
1152
1153 si_async_workunit_release(r);
1154 }
1155
1156 void
1157 si_async_handle_reply(mach_msg_header_t *msg)
1158 {
1159 si_async_workunit_t *r;
1160 mach_port_t reply = msg->msgh_local_port;
1161
1162 r = si_async_worklist_find_unit(reply);
1163 if (r == NULL)
1164 {
1165 #ifdef CALL_TRACE
1166 fprintf(stderr, "** %s can't find worklist item\n", __func__);
1167 #endif
1168 return;
1169 }
1170
1171 #ifdef CALL_TRACE
1172 fprintf(stderr, "** %s worklist item %p flags are 0x%08x\n", __func__, r, r->flags);
1173 #endif
1174 if ((r->flags & WORKUNIT_CANCELLED) == 0)
1175 {
1176 #ifdef CALL_TRACE
1177 fprintf(stderr, "** %s workunit thread is still active\n", __func__);
1178 #endif
1179 return;
1180 }
1181
1182 if (r->callback != NULL)
1183 {
1184 if (r->flags & WORKUNIT_RETURNS_LIST) ((list_async_callback)(r->callback))(r->reslist, r->err, r->context);
1185 else ((item_async_callback)(r->callback))(r->resitem, r->err, r->context);
1186
1187 r->reslist = NULL;
1188 r->resitem = NULL;
1189 }
1190 else
1191 {
1192 #ifdef CALL_TRACE
1193 fprintf(stderr, "** %s workunit has no callback\n", __func__);
1194 #endif
1195 }
1196
1197 si_async_workunit_release(r);
1198 }
1199
1200 char *
1201 si_standardize_mac_address(const char *addr)
1202 {
1203 char e[6][3];
1204 char *out;
1205 struct ether_addr *ether;
1206 int i;
1207
1208 if (addr == NULL) return NULL;
1209
1210 /* ether_aton isn't thread-safe */
1211 pthread_mutex_lock(&module_mutex);
1212
1213 ether = ether_aton(addr);
1214 if (ether == NULL)
1215 {
1216 pthread_mutex_unlock(&module_mutex);
1217 return NULL;
1218 }
1219
1220 for (i = 0; i < 6; i++)
1221 {
1222 if (ether->ether_addr_octet[i] <= 15)
1223 {
1224 sprintf(e[i], "0%x", ether->ether_addr_octet[i]);
1225 }
1226 else
1227 {
1228 sprintf(e[i], "%x", ether->ether_addr_octet[i]);
1229 }
1230 }
1231
1232 pthread_mutex_unlock(&module_mutex);
1233
1234 out = NULL;
1235 asprintf(&out, "%s:%s:%s:%s:%s:%s", e[0], e[1], e[2], e[3], e[4], e[5]);
1236 return out;
1237 }