]>
Commit | Line | Data |
---|---|---|
5958d7c0 | 1 | /* |
a40a14f8 | 2 | * Copyright (c) 2000-2008 Apple Inc. All rights reserved. |
5958d7c0 A |
3 | * |
4 | * @APPLE_LICENSE_HEADER_START@ | |
009ee3c6 | 5 | * |
009ee3c6 A |
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 | |
5958d7c0 A |
15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
009ee3c6 A |
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 | * | |
5958d7c0 A |
21 | * @APPLE_LICENSE_HEADER_END@ |
22 | */ | |
23 | ||
0fae82ee A |
24 | /* |
25 | * Modification History | |
26 | * | |
dbf6a266 A |
27 | * October 30, 2003 Allan Nathanson <ajn@apple.com> |
28 | * - add plugin "stop()" function support | |
29 | * | |
0fae82ee A |
30 | * June 11, 2001 Allan Nathanson <ajn@apple.com> |
31 | * - start using CFBundle code | |
32 | * | |
33 | * June 1, 2001 Allan Nathanson <ajn@apple.com> | |
34 | * - public API conversion | |
35 | * | |
36 | * May 26, 2000 Allan Nathanson <ajn@apple.com> | |
37 | * - initial revision | |
38 | */ | |
39 | ||
5958d7c0 A |
40 | #include <mach-o/dyld.h> |
41 | #include <sys/types.h> | |
42 | #include <sys/stat.h> | |
43 | #include <sys/param.h> | |
edebe297 | 44 | #include <sys/wait.h> |
5958d7c0 | 45 | #include <dirent.h> |
dbf6a266 | 46 | #include <sysexits.h> |
5958d7c0 A |
47 | #include <unistd.h> |
48 | #include <NSSystemDirectories.h> | |
49 | ||
50 | #include "configd.h" | |
dbf6a266 | 51 | #include "configd_server.h" |
0fae82ee | 52 | #include <SystemConfiguration/SCDPlugin.h> |
a5f60add | 53 | void _SCDPluginExecInit(); |
5958d7c0 A |
54 | |
55 | ||
56 | /* | |
0fae82ee | 57 | * path components, extensions, entry points, ... |
5958d7c0 | 58 | */ |
0fae82ee A |
59 | #define BUNDLE_DIRECTORY "/SystemConfiguration" /* [/System/Library]/... */ |
60 | #define BUNDLE_DIR_EXTENSION ".bundle" | |
5958d7c0 | 61 | |
5958d7c0 | 62 | |
dbf6a266 A |
63 | typedef struct { |
64 | CFBundleRef bundle; | |
65 | Boolean loaded; | |
66 | Boolean builtin; | |
edebe297 | 67 | Boolean enabled; |
dbf6a266 A |
68 | Boolean verbose; |
69 | SCDynamicStoreBundleLoadFunction load; | |
70 | SCDynamicStoreBundleStartFunction start; | |
71 | SCDynamicStoreBundlePrimeFunction prime; | |
72 | SCDynamicStoreBundleStopFunction stop; | |
73 | } *bundleInfoRef; | |
74 | ||
75 | ||
76 | // all loaded bundles | |
edebe297 | 77 | static CFMutableArrayRef allBundles = NULL; |
dbf6a266 A |
78 | |
79 | // exiting bundles | |
edebe297 | 80 | static CFMutableDictionaryRef exiting = NULL; |
dbf6a266 A |
81 | |
82 | // plugin CFRunLoopRef | |
edebe297 A |
83 | __private_extern__ |
84 | CFRunLoopRef plugin_runLoop = NULL; | |
dbf6a266 A |
85 | |
86 | ||
dbf6a266 A |
87 | extern SCDynamicStoreBundleLoadFunction load_IPMonitor; |
88 | extern SCDynamicStoreBundlePrimeFunction prime_IPMonitor; | |
89 | extern SCDynamicStoreBundleLoadFunction load_InterfaceNamer; | |
90 | extern SCDynamicStoreBundleLoadFunction load_KernelEventMonitor; | |
91 | extern SCDynamicStoreBundlePrimeFunction prime_KernelEventMonitor; | |
dbf6a266 A |
92 | extern SCDynamicStoreBundleLoadFunction load_LinkConfiguration; |
93 | extern SCDynamicStoreBundleLoadFunction load_PreferencesMonitor; | |
94 | extern SCDynamicStoreBundlePrimeFunction prime_PreferencesMonitor; | |
edebe297 A |
95 | extern SCDynamicStoreBundleLoadFunction load_NetworkIdentification; |
96 | extern SCDynamicStoreBundlePrimeFunction prime_NetworkIdentification; | |
dbf6a266 A |
97 | |
98 | ||
99 | typedef struct { | |
100 | const CFStringRef bundleID; | |
101 | const void *load; // SCDynamicStoreBundleLoadFunction | |
102 | const void *start; // SCDynamicStoreBundleStartFunction | |
103 | const void *prime; // SCDynamicStoreBundlePrimeFunction | |
104 | const void *stop; // SCDynamicStoreBundleStopFunction | |
105 | } builtin, *builtinRef; | |
106 | ||
107 | ||
108 | static const builtin builtin_plugins[] = { | |
dbf6a266 A |
109 | { |
110 | CFSTR("com.apple.SystemConfiguration.IPMonitor"), | |
111 | &load_IPMonitor, | |
112 | NULL, | |
113 | &prime_IPMonitor, | |
114 | NULL | |
115 | }, | |
116 | { | |
117 | CFSTR("com.apple.SystemConfiguration.InterfaceNamer"), | |
118 | &load_InterfaceNamer, | |
119 | NULL, | |
120 | NULL, | |
121 | NULL | |
122 | }, | |
123 | { | |
124 | CFSTR("com.apple.SystemConfiguration.KernelEventMonitor"), | |
125 | &load_KernelEventMonitor, | |
126 | NULL, | |
127 | &prime_KernelEventMonitor, | |
128 | NULL | |
129 | }, | |
dbf6a266 A |
130 | { |
131 | CFSTR("com.apple.SystemConfiguration.LinkConfiguration"), | |
132 | &load_LinkConfiguration, | |
133 | NULL, | |
134 | NULL, | |
135 | NULL | |
136 | }, | |
edebe297 A |
137 | { |
138 | CFSTR("com.apple.SystemConfiguration.NetworkIdentification"), | |
139 | &load_NetworkIdentification, | |
140 | NULL, | |
141 | &prime_NetworkIdentification, | |
a40a14f8 | 142 | NULL |
edebe297 | 143 | }, |
dbf6a266 A |
144 | { |
145 | CFSTR("com.apple.SystemConfiguration.PreferencesMonitor"), | |
146 | &load_PreferencesMonitor, | |
147 | NULL, | |
148 | &prime_PreferencesMonitor, | |
a40a14f8 | 149 | NULL |
5958d7c0 | 150 | } |
dbf6a266 | 151 | }; |
5958d7c0 A |
152 | |
153 | ||
edebe297 | 154 | #ifdef DEBUG |
dbf6a266 | 155 | static void |
edebe297 A |
156 | traceBundle(const char *op, CFBundleRef bundle) |
157 | { | |
158 | if (_configd_trace != NULL) { | |
159 | if (bundle != NULL) { | |
160 | CFStringRef bundleID = CFBundleGetIdentifier(bundle); | |
161 | ||
162 | SCTrace(TRUE, _configd_trace, | |
163 | CFSTR("bundle : %s %@\n"), | |
164 | op, | |
165 | bundleID); | |
166 | } else { | |
167 | SCTrace(TRUE, _configd_trace, | |
168 | CFSTR("bundle : %s\n"), | |
169 | op); | |
170 | } | |
171 | } | |
172 | ||
173 | return; | |
174 | } | |
175 | #endif /* DEBUG */ | |
176 | ||
177 | ||
178 | static void | |
179 | addBundle(CFBundleRef bundle, Boolean forceEnabled) | |
5958d7c0 | 180 | { |
dbf6a266 A |
181 | CFDictionaryRef bundleDict; |
182 | bundleInfoRef bundleInfo; | |
d6c893b2 | 183 | |
dbf6a266 A |
184 | bundleInfo = CFAllocatorAllocate(NULL, sizeof(*bundleInfo), 0); |
185 | bundleInfo->bundle = (CFBundleRef)CFRetain(bundle); | |
186 | bundleInfo->loaded = FALSE; | |
187 | bundleInfo->builtin = FALSE; | |
edebe297 | 188 | bundleInfo->enabled = TRUE; |
dbf6a266 A |
189 | bundleInfo->verbose = FALSE; |
190 | bundleInfo->load = NULL; | |
191 | bundleInfo->start = NULL; | |
192 | bundleInfo->prime = NULL; | |
193 | bundleInfo->stop = NULL; | |
d6c893b2 | 194 | |
dbf6a266 A |
195 | bundleDict = CFBundleGetInfoDictionary(bundle); |
196 | if (isA_CFDictionary(bundleDict)) { | |
197 | CFBooleanRef bVal; | |
198 | ||
199 | bVal = CFDictionaryGetValue(bundleDict, kSCBundleIsBuiltinKey); | |
edebe297 A |
200 | if (isA_CFBoolean(bVal)) { |
201 | bundleInfo->builtin = CFBooleanGetValue(bVal); | |
202 | } | |
203 | ||
204 | bVal = CFDictionaryGetValue(bundleDict, kSCBundleEnabledKey); | |
205 | if (isA_CFBoolean(bVal)) { | |
206 | bundleInfo->enabled = CFBooleanGetValue(bVal); | |
dbf6a266 | 207 | } |
5958d7c0 | 208 | |
dbf6a266 | 209 | bVal = CFDictionaryGetValue(bundleDict, kSCBundleVerboseKey); |
edebe297 A |
210 | if (isA_CFBoolean(bVal)) { |
211 | bundleInfo->verbose = CFBooleanGetValue(bVal); | |
dbf6a266 A |
212 | } |
213 | } | |
5958d7c0 | 214 | |
edebe297 A |
215 | if (forceEnabled) { |
216 | bundleInfo->enabled = TRUE; | |
217 | } | |
218 | ||
dbf6a266 A |
219 | CFArrayAppendValue(allBundles, bundleInfo); |
220 | return; | |
5958d7c0 A |
221 | } |
222 | ||
223 | ||
0fae82ee A |
224 | static CFStringRef |
225 | shortBundleIdentifier(CFStringRef bundleID) | |
5958d7c0 | 226 | { |
dbf6a266 A |
227 | CFIndex len = CFStringGetLength(bundleID); |
228 | CFRange range; | |
0fae82ee A |
229 | CFStringRef shortID = NULL; |
230 | ||
231 | if (CFStringFindWithOptions(bundleID, | |
232 | CFSTR("."), | |
233 | CFRangeMake(0, len), | |
234 | kCFCompareBackwards, | |
235 | &range)) { | |
236 | range.location = range.location + range.length; | |
237 | range.length = len - range.location; | |
238 | shortID = CFStringCreateWithSubstring(NULL, bundleID, range); | |
239 | } | |
5958d7c0 | 240 | |
0fae82ee A |
241 | return shortID; |
242 | } | |
5958d7c0 | 243 | |
5958d7c0 | 244 | |
dbf6a266 A |
245 | static void * |
246 | getBundleSymbol(CFBundleRef bundle, CFStringRef functionName, CFStringRef shortID) | |
247 | { | |
248 | void *func; | |
0fae82ee | 249 | |
dbf6a266 A |
250 | // search for load(), start(), prime(), stop(), ... |
251 | func = CFBundleGetFunctionPointerForName(bundle, functionName); | |
252 | if (func != NULL) { | |
253 | return func; | |
5958d7c0 A |
254 | } |
255 | ||
dbf6a266 A |
256 | if (shortID != NULL) { |
257 | CFStringRef altFunctionName; | |
258 | ||
259 | // search for load_XXX(), ... | |
260 | altFunctionName = CFStringCreateWithFormat(NULL, | |
261 | NULL, | |
262 | CFSTR("%@_%@"), | |
263 | functionName, | |
264 | shortID); | |
265 | func = CFBundleGetFunctionPointerForName(bundle, altFunctionName); | |
266 | CFRelease(altFunctionName); | |
5958d7c0 A |
267 | } |
268 | ||
dbf6a266 A |
269 | return func; |
270 | } | |
5958d7c0 | 271 | |
dbf6a266 | 272 | |
a40a14f8 A |
273 | static const char * |
274 | getBundleDirNameAndPath(CFBundleRef bundle, char *buf, size_t buf_len) | |
275 | { | |
276 | char *cp; | |
277 | int len; | |
278 | Boolean ok; | |
279 | CFURLRef url; | |
280 | ||
281 | url = CFBundleCopyBundleURL(bundle); | |
282 | if (url == NULL) { | |
283 | return NULL; | |
284 | } | |
285 | ||
286 | ok = CFURLGetFileSystemRepresentation(url, TRUE, (UInt8 *)buf, buf_len); | |
287 | CFRelease(url); | |
288 | if (!ok) { | |
289 | return NULL; | |
290 | } | |
291 | ||
292 | cp = strrchr(buf, '/'); | |
293 | if (cp != NULL) { | |
294 | cp++; | |
295 | } else { | |
296 | cp = buf; | |
297 | } | |
298 | ||
299 | /* check if this directory entry is a valid bundle name */ | |
300 | len = strlen(cp); | |
301 | if (len <= (int)sizeof(BUNDLE_DIR_EXTENSION)) { | |
302 | /* if entry name isn't long enough */ | |
303 | return NULL; | |
304 | } | |
305 | ||
306 | len -= sizeof(BUNDLE_DIR_EXTENSION) - 1; | |
307 | if (strcmp(&cp[len], BUNDLE_DIR_EXTENSION) != 0) { | |
308 | /* if entry name doesn't end with ".bundle" */ | |
309 | return NULL; | |
310 | } | |
311 | ||
312 | return cp; | |
313 | } | |
314 | ||
315 | ||
316 | #pragma mark - | |
317 | #pragma mark load | |
318 | ||
319 | ||
320 | static void | |
321 | forkBundle_setup(pid_t pid, void *setupContext) | |
322 | { | |
323 | if (pid == 0) { | |
324 | // if child | |
325 | unsetenv("__LAUNCHD_FD"); | |
326 | setenv("__FORKED_PLUGIN__", "Yes", 1); | |
327 | } | |
328 | ||
329 | return; | |
330 | } | |
331 | ||
332 | ||
333 | static void | |
334 | forkBundle(CFBundleRef bundle, CFStringRef bundleID) | |
335 | { | |
336 | char *argv[] = { "configd", "-d", "-t", NULL, NULL }; | |
337 | const char *name; | |
338 | char path[MAXPATHLEN]; | |
339 | pid_t pid; | |
340 | ||
341 | // get the bundle's path | |
342 | name = getBundleDirNameAndPath(bundle, path, sizeof(path)); | |
343 | if (name == NULL) { | |
344 | SCLog(TRUE, LOG_ERR, CFSTR("skipped %@ (could not determine path)"), bundle); | |
345 | return; | |
346 | } | |
347 | ||
348 | // fork and exec configd opting to load only this plugin | |
349 | argv[3] = path; | |
350 | pid = _SCDPluginExecCommand2(NULL, NULL, 0, 0, "/usr/libexec/configd", argv, forkBundle_setup, NULL); | |
351 | if (pid == -1) { | |
352 | SCLog(TRUE, LOG_ERR, | |
353 | CFSTR("skipped %@ (could not exec child) : %s"), | |
354 | bundle, | |
355 | strerror(errno)); | |
356 | return; | |
357 | } | |
358 | ||
359 | SCLog(TRUE, LOG_NOTICE, CFSTR("forked %@, pid=%d"), bundleID, pid); | |
360 | return; | |
361 | } | |
362 | ||
363 | ||
dbf6a266 A |
364 | static void |
365 | loadBundle(const void *value, void *context) { | |
366 | CFStringRef bundleID; | |
367 | bundleInfoRef bundleInfo = (bundleInfoRef)value; | |
368 | Boolean bundleExclude; | |
369 | CFIndex *nLoaded = (CFIndex *)context; | |
370 | CFStringRef shortID; | |
371 | ||
372 | bundleID = CFBundleGetIdentifier(bundleInfo->bundle); | |
373 | if (bundleID == NULL) { | |
374 | // sorry, no bundles without a bundle identifier | |
a40a14f8 | 375 | SCLog(TRUE, LOG_NOTICE, CFSTR("skipped %@ (no bundle ID)"), bundleInfo->bundle); |
0fae82ee A |
376 | return; |
377 | } | |
5958d7c0 | 378 | |
dbf6a266 | 379 | shortID = shortBundleIdentifier(bundleID); |
5958d7c0 | 380 | |
dbf6a266 | 381 | bundleExclude = CFSetContainsValue(_plugins_exclude, bundleID); |
edebe297 | 382 | if (!bundleExclude) { |
dbf6a266 A |
383 | if (shortID != NULL) { |
384 | bundleExclude = CFSetContainsValue(_plugins_exclude, shortID); | |
385 | } | |
5958d7c0 A |
386 | } |
387 | ||
dbf6a266 A |
388 | if (bundleExclude) { |
389 | // sorry, this bundle has been excluded | |
a40a14f8 | 390 | SCLog(TRUE, LOG_NOTICE, CFSTR("skipped %@ (excluded)"), bundleID); |
edebe297 A |
391 | goto done; |
392 | } | |
393 | ||
394 | if (!bundleInfo->enabled) { | |
a40a14f8 A |
395 | // sorry, this bundle has not been enabled |
396 | SCLog(TRUE, LOG_INFO, CFSTR("skipped %@ (disabled)"), bundleID); | |
397 | goto done; | |
398 | } | |
399 | ||
400 | if (_plugins_fork) { | |
401 | forkBundle(bundleInfo->bundle, bundleID); | |
dbf6a266 | 402 | goto done; |
5958d7c0 A |
403 | } |
404 | ||
dbf6a266 A |
405 | if (!bundleInfo->verbose) { |
406 | bundleInfo->verbose = CFSetContainsValue(_plugins_verbose, bundleID); | |
407 | if (!bundleInfo->verbose) { | |
408 | if (shortID != NULL) { | |
409 | bundleInfo->verbose = CFSetContainsValue(_plugins_verbose, shortID); | |
410 | } | |
411 | } | |
5958d7c0 A |
412 | } |
413 | ||
dbf6a266 | 414 | if (bundleInfo->builtin) { |
a40a14f8 | 415 | int i; |
5958d7c0 | 416 | |
dbf6a266 | 417 | SCLog(TRUE, LOG_DEBUG, CFSTR("adding %@"), bundleID); |
5958d7c0 | 418 | |
dbf6a266 A |
419 | for (i = 0; i < sizeof(builtin_plugins)/sizeof(builtin_plugins[0]); i++) { |
420 | if (CFEqual(bundleID, builtin_plugins[i].bundleID)) { | |
421 | bundleInfo->load = builtin_plugins[i].load; | |
422 | bundleInfo->start = builtin_plugins[i].start; | |
423 | bundleInfo->prime = builtin_plugins[i].prime; | |
424 | bundleInfo->stop = builtin_plugins[i].stop; | |
425 | break; | |
426 | } | |
427 | } | |
edebe297 A |
428 | |
429 | if ((bundleInfo->load == NULL) && | |
430 | (bundleInfo->start == NULL) && | |
431 | (bundleInfo->prime == NULL) && | |
432 | (bundleInfo->stop == NULL)) { | |
433 | SCLog(TRUE, LOG_NOTICE, CFSTR("%@ add failed"), bundleID); | |
434 | goto done; | |
435 | } | |
dbf6a266 | 436 | } else { |
a40a14f8 A |
437 | CFErrorRef error = NULL; |
438 | ||
dbf6a266 | 439 | SCLog(TRUE, LOG_DEBUG, CFSTR("loading %@"), bundleID); |
5958d7c0 | 440 | |
edebe297 A |
441 | #ifdef DEBUG |
442 | traceBundle("loading", bundleInfo->bundle); | |
443 | #endif /* DEBUG */ | |
444 | ||
a40a14f8 A |
445 | if (!CFBundleLoadExecutableAndReturnError(bundleInfo->bundle, &error)) { |
446 | CFStringRef description; | |
447 | ||
448 | description = CFErrorCopyDescription(error); | |
dbf6a266 | 449 | SCLog(TRUE, LOG_NOTICE, CFSTR("%@ load failed"), bundleID); |
a40a14f8 A |
450 | SCLog(TRUE, LOG_NOTICE, CFSTR(" %@"), description); |
451 | CFRelease(description); | |
452 | CFRelease(error); | |
dbf6a266 | 453 | goto done; |
0fae82ee | 454 | } |
dbf6a266 A |
455 | |
456 | // get bundle entry points | |
457 | bundleInfo->load = getBundleSymbol(bundleInfo->bundle, CFSTR("load" ), shortID); | |
458 | bundleInfo->start = getBundleSymbol(bundleInfo->bundle, CFSTR("start"), shortID); | |
459 | bundleInfo->prime = getBundleSymbol(bundleInfo->bundle, CFSTR("prime"), shortID); | |
460 | bundleInfo->stop = getBundleSymbol(bundleInfo->bundle, CFSTR("stop" ), shortID); | |
5958d7c0 A |
461 | } |
462 | ||
dbf6a266 A |
463 | /* mark this bundle as having been loaded */ |
464 | bundleInfo->loaded = TRUE; | |
5958d7c0 | 465 | |
dbf6a266 A |
466 | /* bump the count of loaded bundles */ |
467 | *nLoaded = *nLoaded + 1; | |
468 | ||
469 | done : | |
5958d7c0 | 470 | |
dbf6a266 | 471 | if (shortID != NULL) CFRelease(shortID); |
0fae82ee | 472 | return; |
5958d7c0 A |
473 | } |
474 | ||
475 | ||
dbf6a266 A |
476 | void |
477 | callLoadFunction(const void *value, void *context) { | |
478 | bundleInfoRef bundleInfo = (bundleInfoRef)value; | |
0fae82ee | 479 | |
dbf6a266 | 480 | if (!bundleInfo->loaded) { |
0fae82ee | 481 | return; |
5958d7c0 | 482 | } |
5958d7c0 | 483 | |
dbf6a266 A |
484 | if (bundleInfo->load == NULL) { |
485 | // if no load() function | |
486 | return; | |
487 | } | |
488 | ||
edebe297 A |
489 | #ifdef DEBUG |
490 | traceBundle("calling load() for", bundleInfo->bundle); | |
491 | #endif /* DEBUG */ | |
492 | ||
dbf6a266 | 493 | (*bundleInfo->load)(bundleInfo->bundle, bundleInfo->verbose); |
edebe297 | 494 | |
dbf6a266 A |
495 | return; |
496 | } | |
497 | ||
498 | ||
a40a14f8 A |
499 | #pragma mark - |
500 | #pragma mark start | |
501 | ||
502 | ||
dbf6a266 A |
503 | void |
504 | callStartFunction(const void *value, void *context) { | |
a40a14f8 | 505 | const char *bundleDirName; |
dbf6a266 | 506 | bundleInfoRef bundleInfo = (bundleInfoRef)value; |
dbf6a266 A |
507 | char bundleName[MAXNAMLEN + 1]; |
508 | char bundlePath[MAXPATHLEN]; | |
dbf6a266 | 509 | int len; |
dbf6a266 A |
510 | |
511 | if (!bundleInfo->loaded) { | |
0fae82ee A |
512 | return; |
513 | } | |
5958d7c0 | 514 | |
dbf6a266 A |
515 | if (bundleInfo->start == NULL) { |
516 | // if no start() function | |
0fae82ee A |
517 | return; |
518 | } | |
5958d7c0 | 519 | |
a40a14f8 A |
520 | /* copy the bundle's path */ |
521 | bundleDirName = getBundleDirNameAndPath(bundleInfo->bundle, bundlePath, sizeof(bundlePath)); | |
522 | if (bundleDirName == NULL) { | |
523 | // if we have a problem with the bundle's path | |
5958d7c0 A |
524 | return; |
525 | } | |
526 | ||
a40a14f8 A |
527 | /* copy (just) the bundle's name */ |
528 | if (strlcpy(bundleName, bundleDirName, sizeof(bundleName)) > sizeof(bundleName)) { | |
529 | // if we have a problem with the bundle's name | |
5958d7c0 A |
530 | return; |
531 | } | |
a40a14f8 A |
532 | len = strlen(bundleName) - (sizeof(BUNDLE_DIR_EXTENSION) - 1); |
533 | bundleName[len] = '\0'; | |
5958d7c0 | 534 | |
edebe297 A |
535 | #ifdef DEBUG |
536 | traceBundle("calling start() for", bundleInfo->bundle); | |
537 | #endif /* DEBUG */ | |
538 | ||
dbf6a266 | 539 | (*bundleInfo->start)(bundleName, bundlePath); |
edebe297 | 540 | |
dbf6a266 A |
541 | return; |
542 | } | |
543 | ||
544 | ||
a40a14f8 A |
545 | #pragma mark - |
546 | #pragma mark prime | |
547 | ||
548 | ||
dbf6a266 A |
549 | void |
550 | callPrimeFunction(const void *value, void *context) { | |
551 | bundleInfoRef bundleInfo = (bundleInfoRef)value; | |
552 | ||
553 | if (!bundleInfo->loaded) { | |
554 | return; | |
555 | } | |
556 | ||
557 | if (bundleInfo->prime == NULL) { | |
558 | // if no prime() function | |
559 | return; | |
560 | } | |
561 | ||
edebe297 A |
562 | #ifdef DEBUG |
563 | traceBundle("calling prime() for", bundleInfo->bundle); | |
564 | #endif /* DEBUG */ | |
565 | ||
dbf6a266 | 566 | (*bundleInfo->prime)(); |
edebe297 | 567 | |
5958d7c0 A |
568 | return; |
569 | } | |
570 | ||
571 | ||
a40a14f8 A |
572 | #pragma mark - |
573 | #pragma mark stop | |
574 | ||
575 | ||
5958d7c0 | 576 | static void |
dbf6a266 A |
577 | stopComplete(void *info) |
578 | { | |
579 | CFBundleRef bundle = (CFBundleRef)info; | |
580 | CFStringRef bundleID = CFBundleGetIdentifier(bundle); | |
581 | CFRunLoopSourceRef stopRls; | |
582 | ||
583 | SCLog(TRUE, LOG_DEBUG, CFSTR("** %@ complete (%f)"), bundleID, CFAbsoluteTimeGetCurrent()); | |
584 | ||
585 | stopRls = (CFRunLoopSourceRef)CFDictionaryGetValue(exiting, bundle); | |
a40a14f8 A |
586 | if (stopRls == NULL) { |
587 | return; | |
588 | } | |
589 | ||
dbf6a266 A |
590 | CFRunLoopSourceInvalidate(stopRls); |
591 | ||
592 | CFDictionaryRemoveValue(exiting, bundle); | |
593 | ||
594 | if (CFDictionaryGetCount(exiting) == 0) { | |
595 | int status; | |
596 | ||
597 | // if all of the plugins are happy | |
598 | status = server_shutdown(); | |
599 | SCLog(TRUE, LOG_DEBUG, CFSTR("server shutdown complete (%f)"), CFAbsoluteTimeGetCurrent()); | |
600 | exit (status); | |
601 | } | |
5958d7c0 | 602 | |
dbf6a266 A |
603 | return; |
604 | } | |
605 | ||
606 | ||
607 | static void | |
608 | stopDelayed(CFRunLoopTimerRef timer, void *info) | |
609 | { | |
610 | const void **keys; | |
611 | CFIndex i; | |
612 | CFIndex n; | |
613 | int status; | |
614 | ||
615 | SCLog(TRUE, LOG_ERR, CFSTR("server shutdown was delayed, unresponsive plugins:")); | |
616 | ||
617 | /* | |
618 | * we've asked our plugins to shutdown but someone | |
619 | * isn't listening. | |
620 | */ | |
621 | n = CFDictionaryGetCount(exiting); | |
622 | keys = CFAllocatorAllocate(NULL, n * sizeof(CFTypeRef), 0); | |
623 | CFDictionaryGetKeysAndValues(exiting, keys, NULL); | |
624 | for (i = 0; i < n; i++) { | |
625 | CFBundleRef bundle; | |
626 | CFStringRef bundleID; | |
627 | ||
628 | bundle = (CFBundleRef)keys[i]; | |
629 | bundleID = CFBundleGetIdentifier(bundle); | |
630 | SCLog(TRUE, LOG_ERR, CFSTR("** %@"), bundleID); | |
631 | } | |
632 | CFAllocatorDeallocate(NULL, keys); | |
633 | ||
634 | status = server_shutdown(); | |
635 | exit (status); | |
636 | } | |
637 | ||
edebe297 A |
638 | static CFStringRef |
639 | stopRLSCopyDescription(const void *info) | |
640 | { | |
641 | CFBundleRef bundle = (CFBundleRef)info; | |
642 | ||
643 | return CFStringCreateWithFormat(NULL, | |
644 | NULL, | |
645 | CFSTR("<stopRLS %p> {bundleID = %@}"), | |
646 | info, | |
647 | CFBundleGetIdentifier(bundle)); | |
648 | } | |
649 | ||
650 | ||
dbf6a266 A |
651 | static void |
652 | stopBundle(const void *value, void *context) { | |
653 | bundleInfoRef bundleInfo = (bundleInfoRef)value; | |
654 | CFRunLoopSourceRef stopRls; | |
edebe297 A |
655 | CFRunLoopSourceContext stopContext = { 0 // version |
656 | , bundleInfo->bundle // info | |
657 | , CFRetain // retain | |
658 | , CFRelease // release | |
659 | , stopRLSCopyDescription // copyDescription | |
660 | , CFEqual // equal | |
661 | , CFHash // hash | |
662 | , NULL // schedule | |
663 | , NULL // cancel | |
664 | , stopComplete // perform | |
dbf6a266 A |
665 | }; |
666 | ||
667 | if (!bundleInfo->loaded) { | |
5958d7c0 A |
668 | return; |
669 | } | |
670 | ||
dbf6a266 A |
671 | if (bundleInfo->stop == NULL) { |
672 | // if no stop() function | |
0fae82ee | 673 | return; |
5958d7c0 A |
674 | } |
675 | ||
dbf6a266 A |
676 | stopRls = CFRunLoopSourceCreate(NULL, 0, &stopContext); |
677 | CFRunLoopAddSource(CFRunLoopGetCurrent(), stopRls, kCFRunLoopDefaultMode); | |
678 | CFDictionaryAddValue(exiting, bundleInfo->bundle, stopRls); | |
679 | CFRelease(stopRls); | |
680 | ||
681 | (*bundleInfo->stop)(stopRls); | |
682 | ||
683 | return; | |
684 | } | |
685 | ||
686 | ||
687 | static void | |
688 | stopBundles() | |
689 | { | |
690 | /* | |
691 | * If defined, call each bundles stop() function. This function is | |
692 | * called when configd has been asked to shut down (via a SIGTERM). The | |
693 | * function should signal the provided run loop source when it is "ready" | |
694 | * for the shut down to proceeed. | |
695 | */ | |
696 | SCLog(_configd_verbose, LOG_DEBUG, CFSTR("calling bundle stop() functions")); | |
697 | CFArrayApplyFunction(allBundles, | |
698 | CFRangeMake(0, CFArrayGetCount(allBundles)), | |
699 | stopBundle, | |
700 | NULL); | |
701 | ||
702 | if (CFDictionaryGetCount(exiting) == 0) { | |
703 | int status; | |
704 | ||
705 | // if all of the plugins are happy | |
706 | status = server_shutdown(); | |
707 | SCLog(TRUE, LOG_DEBUG, CFSTR("server shutdown complete (%f)"), CFAbsoluteTimeGetCurrent()); | |
708 | exit (status); | |
709 | } else { | |
710 | CFRunLoopTimerRef timer; | |
711 | ||
712 | /* sorry, we're not going to wait longer than 20 seconds */ | |
713 | timer = CFRunLoopTimerCreate(NULL, /* allocator */ | |
714 | CFAbsoluteTimeGetCurrent() + 20.0, /* fireDate (in 20 seconds) */ | |
715 | 0.0, /* interval (== one-shot) */ | |
716 | 0, /* flags */ | |
717 | 0, /* order */ | |
718 | stopDelayed, /* callout */ | |
719 | NULL); /* context */ | |
720 | CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopDefaultMode); | |
721 | CFRelease(timer); | |
722 | } | |
723 | ||
5958d7c0 A |
724 | return; |
725 | } | |
726 | ||
727 | ||
a40a14f8 A |
728 | #pragma mark - |
729 | #pragma mark term | |
730 | ||
731 | ||
edebe297 A |
732 | static CFStringRef |
733 | termRLSCopyDescription(const void *info) | |
734 | { | |
735 | return CFStringCreateWithFormat(NULL, NULL, CFSTR("<SIGTERM RLS>")); | |
736 | } | |
737 | ||
738 | ||
dbf6a266 A |
739 | __private_extern__ |
740 | Boolean | |
741 | plugin_term(int *status) | |
742 | { | |
edebe297 A |
743 | CFRunLoopSourceContext termContext = { 0 // version |
744 | , (void *)1 // info | |
745 | , NULL // retain | |
746 | , NULL // release | |
747 | , termRLSCopyDescription // copyDescription | |
748 | , NULL // equal | |
749 | , NULL // hash | |
750 | , NULL // schedule | |
751 | , NULL // cancel | |
752 | , stopBundles // perform | |
dbf6a266 | 753 | }; |
edebe297 | 754 | CFRunLoopSourceRef termRls; |
dbf6a266 A |
755 | |
756 | if (plugin_runLoop == NULL) { | |
757 | // if no plugins | |
758 | *status = EX_OK; | |
759 | return FALSE; // don't delay shutdown | |
760 | } | |
761 | ||
762 | if (exiting != NULL) { | |
763 | // if shutdown already active | |
764 | return TRUE; | |
765 | } | |
766 | ||
767 | SCLog(TRUE, LOG_DEBUG, CFSTR("starting server shutdown (%f)"), CFAbsoluteTimeGetCurrent()); | |
768 | ||
769 | exiting = CFDictionaryCreateMutable(NULL, | |
770 | 0, | |
771 | &kCFTypeDictionaryKeyCallBacks, | |
772 | &kCFTypeDictionaryValueCallBacks); | |
773 | ||
edebe297 A |
774 | termRls = CFRunLoopSourceCreate(NULL, 0, &termContext); |
775 | CFRunLoopAddSource(plugin_runLoop, termRls, kCFRunLoopDefaultMode); | |
776 | CFRunLoopSourceSignal(termRls); | |
777 | CFRelease(termRls); | |
dbf6a266 A |
778 | CFRunLoopWakeUp(plugin_runLoop); |
779 | ||
780 | return TRUE; | |
781 | } | |
782 | ||
783 | ||
a40a14f8 A |
784 | #pragma mark - |
785 | #pragma mark initialization | |
786 | ||
787 | ||
a5f60add | 788 | #ifdef DEBUG |
0fae82ee | 789 | static void |
5958d7c0 A |
790 | timerCallback(CFRunLoopTimerRef timer, void *info) |
791 | { | |
edebe297 A |
792 | static int pass = 0; |
793 | ||
794 | pass++; | |
795 | if ((pass > 120) && ((pass % 60) != 0)) { | |
796 | return; | |
797 | } | |
798 | ||
799 | traceBundle("the [plugin] CFRunLoop is waiting...", NULL); | |
0fae82ee A |
800 | return; |
801 | } | |
a5f60add A |
802 | #endif /* DEBUG */ |
803 | ||
0fae82ee A |
804 | |
805 | static void | |
806 | sortBundles(CFMutableArrayRef orig) | |
807 | { | |
dbf6a266 | 808 | CFMutableArrayRef new; |
0fae82ee | 809 | |
dbf6a266 | 810 | new = CFArrayCreateMutable(NULL, 0, NULL); |
0fae82ee A |
811 | while (CFArrayGetCount(orig) > 0) { |
812 | int i; | |
009ee3c6 A |
813 | Boolean inserted = FALSE; |
814 | int nOrig = CFArrayGetCount(orig); | |
0fae82ee | 815 | |
009ee3c6 | 816 | for (i = 0; i < nOrig; i++) { |
dbf6a266 A |
817 | bundleInfoRef bundleInfo1 = (bundleInfoRef)CFArrayGetValueAtIndex(orig, i); |
818 | CFStringRef bundleID1 = CFBundleGetIdentifier(bundleInfo1->bundle); | |
0fae82ee A |
819 | int count; |
820 | CFDictionaryRef dict; | |
821 | int j; | |
009ee3c6 | 822 | int nRequires; |
0fae82ee A |
823 | CFArrayRef requires = NULL; |
824 | ||
dbf6a266 | 825 | dict = isA_CFDictionary(CFBundleGetInfoDictionary(bundleInfo1->bundle)); |
0fae82ee | 826 | if (dict) { |
dbf6a266 | 827 | requires = CFDictionaryGetValue(dict, kSCBundleRequiresKey); |
0fae82ee A |
828 | requires = isA_CFArray(requires); |
829 | } | |
830 | if (bundleID1 == NULL || requires == NULL) { | |
dbf6a266 | 831 | CFArrayInsertValueAtIndex(new, 0, bundleInfo1); |
0fae82ee A |
832 | CFArrayRemoveValueAtIndex(orig, i); |
833 | inserted = TRUE; | |
834 | break; | |
835 | } | |
009ee3c6 A |
836 | count = nRequires = CFArrayGetCount(requires); |
837 | for (j = 0; j < nRequires; j++) { | |
0fae82ee | 838 | int k; |
009ee3c6 | 839 | int nNew; |
0fae82ee A |
840 | CFStringRef r = CFArrayGetValueAtIndex(requires, j); |
841 | ||
009ee3c6 A |
842 | nNew = CFArrayGetCount(new); |
843 | for (k = 0; k < nNew; k++) { | |
dbf6a266 A |
844 | bundleInfoRef bundleInfo2 = (bundleInfoRef)CFArrayGetValueAtIndex(new, k); |
845 | CFStringRef bundleID2 = CFBundleGetIdentifier(bundleInfo2->bundle); | |
0fae82ee A |
846 | |
847 | if (bundleID2 && CFEqual(bundleID2, r)) { | |
848 | count--; | |
849 | } | |
850 | } | |
851 | } | |
852 | if (count == 0) { | |
853 | /* all dependencies are met, append */ | |
dbf6a266 | 854 | CFArrayAppendValue(new, bundleInfo1); |
0fae82ee A |
855 | CFArrayRemoveValueAtIndex(orig, i); |
856 | inserted = TRUE; | |
857 | break; | |
858 | } | |
859 | } | |
860 | ||
861 | if (inserted == FALSE) { | |
862 | SCLog(TRUE, LOG_NOTICE, CFSTR("Bundles have circular dependency!!!")); | |
863 | break; | |
864 | } | |
865 | } | |
866 | if (CFArrayGetCount(orig) > 0) { | |
867 | /* we have a circular dependency, append remaining items on new array */ | |
868 | CFArrayAppendArray(new, orig, CFRangeMake(0, CFArrayGetCount(orig))); | |
869 | } | |
870 | else { | |
871 | /* new one is a sorted version of original */ | |
872 | } | |
873 | ||
874 | CFArrayRemoveAllValues(orig); | |
875 | CFArrayAppendArray(orig, new, CFRangeMake(0, CFArrayGetCount(new))); | |
876 | CFRelease(new); | |
5958d7c0 A |
877 | return; |
878 | } | |
879 | ||
880 | ||
009ee3c6 | 881 | __private_extern__ |
5958d7c0 A |
882 | void * |
883 | plugin_exec(void *arg) | |
884 | { | |
dbf6a266 | 885 | CFIndex nLoaded = 0; |
5958d7c0 | 886 | |
0fae82ee | 887 | /* keep track of bundles */ |
dbf6a266 | 888 | allBundles = CFArrayCreateMutable(NULL, 0, NULL); |
5958d7c0 | 889 | |
009ee3c6 A |
890 | /* allow plug-ins to exec child/helper processes */ |
891 | _SCDPluginExecInit(); | |
a5f60add | 892 | |
5958d7c0 | 893 | if (arg == NULL) { |
0fae82ee A |
894 | char path[MAXPATHLEN]; |
895 | NSSearchPathEnumerationState state; | |
896 | ||
5958d7c0 | 897 | /* |
0fae82ee | 898 | * identify and load all bundles |
5958d7c0 A |
899 | */ |
900 | state = NSStartSearchPathEnumeration(NSLibraryDirectory, | |
dbf6a266 | 901 | NSSystemDomainMask); |
5958d7c0 | 902 | while ((state = NSGetNextSearchPathEnumeration(state, path))) { |
0fae82ee A |
903 | CFArrayRef bundles; |
904 | CFURLRef url; | |
905 | ||
906 | /* load any available bundle */ | |
edebe297 | 907 | strlcat(path, BUNDLE_DIRECTORY, sizeof(path)); |
0fae82ee A |
908 | SCLog(_configd_verbose, LOG_DEBUG, CFSTR("searching for bundles in \".\"")); |
909 | url = CFURLCreateFromFileSystemRepresentation(NULL, | |
d6c893b2 | 910 | (UInt8 *)path, |
0fae82ee A |
911 | strlen(path), |
912 | TRUE); | |
913 | bundles = CFBundleCreateBundlesFromDirectory(NULL, url, CFSTR(".bundle")); | |
914 | CFRelease(url); | |
915 | ||
dbf6a266 A |
916 | if (bundles != NULL) { |
917 | CFIndex i; | |
918 | CFIndex n; | |
919 | ||
920 | n = CFArrayGetCount(bundles); | |
921 | for (i = 0; i < n; i++) { | |
922 | CFBundleRef bundle; | |
d6c893b2 | 923 | |
dbf6a266 | 924 | bundle = (CFBundleRef)CFArrayGetValueAtIndex(bundles, i); |
edebe297 | 925 | addBundle(bundle, FALSE); |
dbf6a266 | 926 | } |
0fae82ee A |
927 | CFRelease(bundles); |
928 | } | |
5958d7c0 A |
929 | } |
930 | ||
0fae82ee | 931 | sortBundles(allBundles); |
5958d7c0 | 932 | } else { |
0fae82ee A |
933 | CFBundleRef bundle; |
934 | CFURLRef url; | |
935 | ||
5958d7c0 | 936 | /* |
0fae82ee | 937 | * load (only) the specified bundle |
5958d7c0 | 938 | */ |
0fae82ee | 939 | url = CFURLCreateFromFileSystemRepresentation(NULL, |
d6c893b2 | 940 | (UInt8 *)arg, |
0fae82ee A |
941 | strlen((char *)arg), |
942 | TRUE); | |
943 | bundle = CFBundleCreate(NULL, url); | |
dbf6a266 | 944 | if (bundle != NULL) { |
edebe297 | 945 | addBundle(bundle, TRUE); |
0fae82ee A |
946 | CFRelease(bundle); |
947 | } | |
948 | CFRelease(url); | |
949 | } | |
5958d7c0 | 950 | |
edebe297 A |
951 | #ifdef DEBUG |
952 | traceBundle("before loading any plugins", NULL); | |
953 | #endif /* DEBUG */ | |
954 | ||
0fae82ee | 955 | /* |
dbf6a266 A |
956 | * load each bundle. |
957 | */ | |
958 | SCLog(_configd_verbose, LOG_DEBUG, CFSTR("loading bundles")); | |
959 | CFArrayApplyFunction(allBundles, | |
960 | CFRangeMake(0, CFArrayGetCount(allBundles)), | |
961 | loadBundle, | |
962 | &nLoaded); | |
963 | ||
964 | /* | |
965 | * If defined, call each bundles load() function. This function (or | |
966 | * the start() function) should initialize any variables, open any | |
967 | * sessions with "configd", and register any needed notifications. | |
0fae82ee A |
968 | * |
969 | * Note: Establishing initial information in the store should be | |
970 | * deferred until the prime() initialization function so that | |
971 | * any bundles which want to receive a notification that the | |
972 | * data has changed will have an opportunity to install a | |
973 | * notification handler. | |
974 | */ | |
975 | SCLog(_configd_verbose, LOG_DEBUG, CFSTR("calling bundle load() functions")); | |
976 | CFArrayApplyFunction(allBundles, | |
977 | CFRangeMake(0, CFArrayGetCount(allBundles)), | |
dbf6a266 A |
978 | callLoadFunction, |
979 | NULL); | |
5958d7c0 | 980 | |
a40a14f8 A |
981 | if (nLoaded == 0) { |
982 | // if no bundles loaded | |
983 | goto done; | |
984 | } | |
985 | ||
0fae82ee A |
986 | /* |
987 | * If defined, call each bundles start() function. This function is | |
988 | * called after the bundle has been loaded and its load() function has | |
989 | * been called. It should initialize any variables, open any sessions | |
990 | * with "configd", and register any needed notifications. | |
991 | * | |
992 | * Note: Establishing initial information in the store should be | |
993 | * deferred until the prime() initialization function so that | |
994 | * any bundles which want to receive a notification that the | |
995 | * data has changed will have an opportunity to install a | |
996 | * notification handler. | |
997 | */ | |
998 | SCLog(_configd_verbose, LOG_DEBUG, CFSTR("calling bundle start() functions")); | |
999 | CFArrayApplyFunction(allBundles, | |
1000 | CFRangeMake(0, CFArrayGetCount(allBundles)), | |
dbf6a266 | 1001 | callStartFunction, |
0fae82ee | 1002 | NULL); |
5958d7c0 | 1003 | |
0fae82ee A |
1004 | /* |
1005 | * If defined, call each bundles prime() function. This function is | |
1006 | * called after the bundle has been loaded and its load() and start() | |
1007 | * functions have been called. It should initialize any configuration | |
1008 | * information and/or state in the store. | |
1009 | */ | |
1010 | SCLog(_configd_verbose, LOG_DEBUG, CFSTR("calling bundle prime() functions")); | |
1011 | CFArrayApplyFunction(allBundles, | |
1012 | CFRangeMake(0, CFArrayGetCount(allBundles)), | |
dbf6a266 | 1013 | callPrimeFunction, |
0fae82ee | 1014 | NULL); |
5958d7c0 | 1015 | |
a5f60add | 1016 | #ifdef DEBUG |
0fae82ee A |
1017 | if (arg == NULL && (nLoaded > 0)) { |
1018 | CFRunLoopTimerRef timer; | |
5958d7c0 A |
1019 | |
1020 | /* allocate a periodic event (to help show we're not blocking) */ | |
a5f60add A |
1021 | timer = CFRunLoopTimerCreate(NULL, /* allocator */ |
1022 | CFAbsoluteTimeGetCurrent() + 1.0, /* fireDate */ | |
edebe297 | 1023 | 1.0, /* interval */ |
a5f60add A |
1024 | 0, /* flags */ |
1025 | 0, /* order */ | |
1026 | timerCallback, /* callout */ | |
1027 | NULL); /* context */ | |
1028 | CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopDefaultMode); | |
1029 | CFRelease(timer); | |
1030 | } | |
1031 | #endif /* DEBUG */ | |
5958d7c0 | 1032 | |
edebe297 A |
1033 | #ifdef DEBUG |
1034 | traceBundle("about to start plugin CFRunLoop", NULL); | |
1035 | #endif /* DEBUG */ | |
1036 | ||
5958d7c0 A |
1037 | /* |
1038 | * The assumption is that each loaded plugin will establish CFMachPortRef, | |
1039 | * CFSocketRef, and CFRunLoopTimerRef input sources to handle any events | |
1040 | * and register these sources with this threads run loop. If the plugin | |
1041 | * needs to wait and/or block at any time it should do so only in its a | |
1042 | * private thread. | |
1043 | */ | |
0fae82ee | 1044 | SCLog(_configd_verbose, LOG_DEBUG, CFSTR("starting plugin CFRunLoop")); |
dbf6a266 | 1045 | plugin_runLoop = CFRunLoopGetCurrent(); |
5958d7c0 | 1046 | CFRunLoopRun(); |
dbf6a266 | 1047 | |
a40a14f8 A |
1048 | done : |
1049 | ||
dbf6a266 A |
1050 | SCLog(_configd_verbose, LOG_INFO, CFSTR("No more work for the \"configd\" plugins")); |
1051 | plugin_runLoop = NULL; | |
5958d7c0 A |
1052 | return NULL; |
1053 | } | |
1054 | ||
1055 | ||
009ee3c6 | 1056 | __private_extern__ |
5958d7c0 A |
1057 | void |
1058 | plugin_init() | |
1059 | { | |
1060 | pthread_attr_t tattr; | |
1061 | pthread_t tid; | |
1062 | ||
0fae82ee | 1063 | SCLog(_configd_verbose, LOG_DEBUG, CFSTR("Starting thread for plug-ins...")); |
5958d7c0 A |
1064 | pthread_attr_init(&tattr); |
1065 | pthread_attr_setscope(&tattr, PTHREAD_SCOPE_SYSTEM); | |
1066 | pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED); | |
1067 | // pthread_attr_setstacksize(&tattr, 96 * 1024); // each thread gets a 96K stack | |
1068 | pthread_create(&tid, &tattr, plugin_exec, NULL); | |
1069 | pthread_attr_destroy(&tattr); | |
0fae82ee | 1070 | SCLog(_configd_verbose, LOG_DEBUG, CFSTR(" thread id=0x%08x"), tid); |
5958d7c0 A |
1071 | |
1072 | return; | |
1073 | } |