2 * StartupItems.c - Startup Item management routines
3 * Wilfredo Sanchez | wsanchez@opensource.apple.com
4 * Kevin Van Vechten | kevinvv@uclink4.berkeley.edu
7 * Copyright (c) 1999-2002 Apple Computer, Inc. All rights reserved.
9 * @APPLE_APACHE_LICENSE_HEADER_START@
11 * Licensed under the Apache License, Version 2.0 (the "License");
12 * you may not use this file except in compliance with the License.
13 * You may obtain a copy of the License at
15 * http://www.apache.org/licenses/LICENSE-2.0
17 * Unless required by applicable law or agreed to in writing, software
18 * distributed under the License is distributed on an "AS IS" BASIS,
19 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 * See the License for the specific language governing permissions and
21 * limitations under the License.
23 * @APPLE_APACHE_LICENSE_HEADER_END@
27 #include <sys/types.h>
29 #include <sys/sysctl.h>
39 #include <CoreFoundation/CoreFoundation.h>
40 #include "StartupItems.h"
42 #define kStartupItemsPath "/StartupItems"
43 #define kParametersFile "StartupParameters.plist"
44 #define kDisabledFile ".disabled"
46 #define kRunSuccess CFSTR("success")
47 #define kRunFailure CFSTR("failure")
49 static const char *argumentForAction(Action anAction
)
63 #define checkTypeOfValue(aKey,aTypeID) \
65 CFStringRef aProperty = CFDictionaryGetValue(aConfig, aKey); \
66 if (aProperty && CFGetTypeID(aProperty) != aTypeID) \
70 static int StartupItemValidate(CFDictionaryRef aConfig
)
72 if (aConfig
&& CFGetTypeID(aConfig
) == CFDictionaryGetTypeID()) {
73 checkTypeOfValue(kProvidesKey
, CFArrayGetTypeID());
74 checkTypeOfValue(kRequiresKey
, CFArrayGetTypeID());
82 * remove item from waiting list
84 void RemoveItemFromWaitingList(StartupContext aStartupContext
, CFMutableDictionaryRef anItem
)
86 /* Remove the item from the waiting list. */
87 if (aStartupContext
&& anItem
&& aStartupContext
->aWaitingList
) {
88 CFRange aRange
= { 0, CFArrayGetCount(aStartupContext
->aWaitingList
) };
89 CFIndex anIndex
= CFArrayGetFirstIndexOfValue(aStartupContext
->aWaitingList
, aRange
, anItem
);
92 CFArrayRemoveValueAtIndex(aStartupContext
->aWaitingList
, anIndex
);
98 * add item to failed list, create list if it doesn't exist
99 * return and fail quietly if it can't create list
101 void AddItemToFailedList(StartupContext aStartupContext
, CFMutableDictionaryRef anItem
)
103 if (aStartupContext
&& anItem
) {
104 /* create the failed list if it doesn't exist */
105 if (!aStartupContext
->aFailedList
) {
106 aStartupContext
->aFailedList
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
108 if (aStartupContext
->aFailedList
) {
109 CFArrayAppendValue(aStartupContext
->aFailedList
, anItem
);
115 * startupItemListCopyMatches returns an array of items which contain the string aService in the key aKey
117 static CFMutableArrayRef
startupItemListCopyMatches(CFArrayRef anItemList
, CFStringRef aKey
, CFStringRef aService
)
119 CFMutableArrayRef aResult
= NULL
;
121 if (anItemList
&& aKey
&& aService
) {
122 CFIndex anItemCount
= CFArrayGetCount(anItemList
);
123 CFIndex anItemIndex
= 0;
125 aResult
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
127 for (anItemIndex
= 0; anItemIndex
< anItemCount
; ++anItemIndex
) {
128 CFMutableDictionaryRef anItem
= (CFMutableDictionaryRef
) CFArrayGetValueAtIndex(anItemList
, anItemIndex
);
129 CFArrayRef aList
= CFDictionaryGetValue(anItem
, aKey
);
132 if (CFArrayContainsValue(aList
, CFRangeMake(0, CFArrayGetCount(aList
)), aService
) &&
133 !CFArrayContainsValue(aResult
, CFRangeMake(0, CFArrayGetCount(aResult
)), anItem
)) {
134 CFArrayAppendValue(aResult
, anItem
);
142 static void SpecialCasesStartupItemHandler(CFMutableDictionaryRef aConfig
)
144 static const CFStringRef stubitems
[] = {
146 CFSTR("System Tuning"),
147 CFSTR("SecurityServer"),
153 CFSTR("NetworkExtensions"),
154 CFSTR("DirectoryServices"),
155 CFSTR("Network Configuration"),
156 CFSTR("mDNSResponder"),
158 CFSTR("Core Graphics"),
159 CFSTR("Core Services"),
166 CFMutableArrayRef aList
, aNewList
;
168 CFStringRef ci
, type
= kRequiresKey
;
169 const CFStringRef
*c
;
172 aList
= (CFMutableArrayRef
) CFDictionaryGetValue(aConfig
, type
);
174 aCount
= CFArrayGetCount(aList
);
176 aNewList
= CFArrayCreateMutable(kCFAllocatorDefault
, aCount
, &kCFTypeArrayCallBacks
);
178 for (i
= 0; i
< aCount
; i
++) {
179 ci
= CFArrayGetValueAtIndex(aList
, i
);
180 CF_syslog(LOG_DEBUG
, CFSTR("%@: Evaluating %@"), type
, ci
);
181 for (c
= stubitems
; *c
; c
++) {
187 CFArrayAppendValue(aNewList
, ci
);
188 CF_syslog(LOG_DEBUG
, CFSTR("%@: Keeping %@"), type
, ci
);
192 CFDictionaryReplaceValue(aConfig
, type
, aNewList
);
195 if (type
== kUsesKey
)
201 CFIndex
StartupItemListCountServices(CFArrayRef anItemList
)
206 CFIndex anItemCount
= CFArrayGetCount(anItemList
);
207 CFIndex anItemIndex
= 0;
209 for (anItemIndex
= 0; anItemIndex
< anItemCount
; ++anItemIndex
) {
210 CFDictionaryRef anItem
= CFArrayGetValueAtIndex(anItemList
, anItemIndex
);
211 CFArrayRef aProvidesList
= CFDictionaryGetValue(anItem
, kProvidesKey
);
214 aResult
+= CFArrayGetCount(aProvidesList
);
220 bool StartupItemSecurityCheck(const char *aPath
)
222 static struct timeval boot_time
;
223 struct stat aStatBuf
;
226 if (boot_time
.tv_sec
== 0) {
227 int mib
[] = { CTL_KERN
, KERN_BOOTTIME
};
228 size_t boot_time_sz
= sizeof(boot_time
);
231 rv
= sysctl(mib
, sizeof(mib
) / sizeof(mib
[0]), &boot_time
, &boot_time_sz
, NULL
, 0);
234 assert(boot_time_sz
== sizeof(boot_time
));
237 /* should use lstatx_np() on Tiger? */
238 if (lstat(aPath
, &aStatBuf
) == -1) {
240 syslog(LOG_ERR
, "lstat(\"%s\"): %m", aPath
);
244 * We check the boot time because of 5409386.
245 * We ignore the boot time if PPID != 1 because of 5503536.
247 if ((aStatBuf
.st_ctimespec
.tv_sec
> boot_time
.tv_sec
) && (getppid() == 1)) {
248 syslog(LOG_WARNING
, "\"%s\" failed sanity check: path was created after boot up", aPath
);
251 if (!(S_ISREG(aStatBuf
.st_mode
) || S_ISDIR(aStatBuf
.st_mode
))) {
252 syslog(LOG_WARNING
, "\"%s\" failed security check: not a directory or regular file", aPath
);
255 if (aStatBuf
.st_mode
& S_IWOTH
) {
256 syslog(LOG_WARNING
, "\"%s\" failed security check: world writable", aPath
);
259 if (aStatBuf
.st_mode
& S_IWGRP
) {
260 syslog(LOG_WARNING
, "\"%s\" failed security check: group writable", aPath
);
263 if (aStatBuf
.st_uid
!= 0) {
264 syslog(LOG_WARNING
, "\"%s\" failed security check: not owned by UID 0", aPath
);
267 if (aStatBuf
.st_gid
!= 0) {
268 syslog(LOG_WARNING
, "\"%s\" failed security check: not owned by GID 0", aPath
);
272 mkdir(kFixerDir
, ACCESSPERMS
);
273 close(open(kFixerPath
, O_RDWR
|O_CREAT
|O_NOCTTY
, DEFFILEMODE
));
278 CFMutableArrayRef
StartupItemListCreateWithMask(NSSearchPathDomainMask aMask
)
280 CFMutableArrayRef anItemList
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
282 char aPath
[PATH_MAX
];
283 CFIndex aDomainIndex
= 0;
285 NSSearchPathEnumerationState aState
= NSStartSearchPathEnumeration(NSLibraryDirectory
, aMask
);
287 while ((aState
= NSGetNextSearchPathEnumeration(aState
, aPath
))) {
290 strlcat(aPath
, kStartupItemsPath
, sizeof(aPath
));
297 mkdir(aPath
, S_IRWXU
|S_IRGRP
|S_IXGRP
|S_IROTH
|S_IXOTH
);
299 if (!StartupItemSecurityCheck(aPath
))
302 if ((aDirectory
= opendir(aPath
))) {
303 struct dirent
*aBundle
;
305 while ((aBundle
= readdir(aDirectory
))) {
306 struct stat aStatBuf
;
307 char *aBundleName
= aBundle
->d_name
;
308 char aBundlePath
[PATH_MAX
];
309 char aBundleExecutablePath
[PATH_MAX
];
310 char aConfigFile
[PATH_MAX
];
311 char aDisabledFile
[PATH_MAX
];
313 if (aBundleName
[0] == '.')
316 syslog(LOG_DEBUG
, "Found item: %s", aBundleName
);
318 sprintf(aBundlePath
, "%s/%s", aPath
, aBundleName
);
319 sprintf(aBundleExecutablePath
, "%s/%s", aBundlePath
, aBundleName
);
320 sprintf(aConfigFile
, "%s/%s", aBundlePath
, kParametersFile
);
321 sprintf(aDisabledFile
, "%s/%s", aBundlePath
, kDisabledFile
);
323 if (lstat(aDisabledFile
, &aStatBuf
) == 0) {
324 syslog(LOG_NOTICE
, "Skipping disabled StartupItem: %s", aBundlePath
);
327 if (!StartupItemSecurityCheck(aBundlePath
))
329 if (!StartupItemSecurityCheck(aBundleExecutablePath
))
331 if (!StartupItemSecurityCheck(aConfigFile
))
334 /* Stow away the plist data for each bundle */
336 int aConfigFileDescriptor
;
338 if ((aConfigFileDescriptor
= open(aConfigFile
, O_RDONLY
|O_NOCTTY
, (mode_t
) 0)) != -1) {
339 struct stat aConfigFileStatBuffer
;
341 if (stat(aConfigFile
, &aConfigFileStatBuffer
) != -1) {
342 off_t aConfigFileContentsSize
= aConfigFileStatBuffer
.st_size
;
343 char *aConfigFileContentsBuffer
;
345 if ((aConfigFileContentsBuffer
=
346 mmap((caddr_t
) 0, aConfigFileContentsSize
,
347 PROT_READ
, MAP_FILE
| MAP_PRIVATE
,
348 aConfigFileDescriptor
, (off_t
) 0)) != (caddr_t
) - 1) {
349 CFDataRef aConfigData
= NULL
;
350 CFMutableDictionaryRef aConfig
= NULL
;
353 CFDataCreateWithBytesNoCopy(NULL
,
354 (const UInt8
*)aConfigFileContentsBuffer
,
355 aConfigFileContentsSize
,
359 aConfig
= (CFMutableDictionaryRef
)
360 CFPropertyListCreateFromXMLData(NULL
, aConfigData
,
361 kCFPropertyListMutableContainers
,
364 if (StartupItemValidate(aConfig
)) {
365 CFStringRef aBundlePathString
=
366 CFStringCreateWithCString(NULL
, aBundlePath
,
367 kCFStringEncodingUTF8
);
369 CFNumberRef aDomainNumber
=
370 CFNumberCreate(NULL
, kCFNumberCFIndexType
,
373 CFDictionarySetValue(aConfig
, kBundlePathKey
,
375 CFDictionarySetValue(aConfig
, kDomainKey
, aDomainNumber
);
376 CFRelease(aDomainNumber
);
377 SpecialCasesStartupItemHandler(aConfig
);
378 CFArrayAppendValue(anItemList
, aConfig
);
380 CFRelease(aBundlePathString
);
382 syslog(LOG_ERR
, "Malformatted parameters file: %s",
389 CFRelease(aConfigData
);
391 if (munmap(aConfigFileContentsBuffer
, aConfigFileContentsSize
) ==
394 "Unable to unmap parameters file %s for item %s: %m",
395 aConfigFile
, aBundleName
);
399 "Unable to map parameters file %s for item %s: %m",
400 aConfigFile
, aBundleName
);
403 syslog(LOG_ERR
, "Unable to stat parameters file %s for item %s: %m",
404 aConfigFile
, aBundleName
);
407 if (close(aConfigFileDescriptor
) == -1) {
408 syslog(LOG_ERR
, "Unable to close parameters file %s for item %s: %m",
409 aConfigFile
, aBundleName
);
412 syslog(LOG_ERR
, "Unable to open parameters file %s for item %s: %m", aConfigFile
,
417 if (closedir(aDirectory
) == -1) {
418 syslog(LOG_WARNING
, "Unable to directory bundle %s: %m", aPath
);
421 if (errno
!= ENOENT
) {
422 syslog(LOG_WARNING
, "Open on directory %s failed: %m", aPath
);
431 CFMutableDictionaryRef
StartupItemListGetProvider(CFArrayRef anItemList
, CFStringRef aService
)
433 CFMutableDictionaryRef aResult
= NULL
;
434 CFMutableArrayRef aList
= startupItemListCopyMatches(anItemList
, kProvidesKey
, aService
);
436 if (aList
&& CFArrayGetCount(aList
) > 0)
437 aResult
= (CFMutableDictionaryRef
) CFArrayGetValueAtIndex(aList
, 0);
439 if (aList
) CFRelease(aList
);
444 CFArrayRef
StartupItemListGetRunning(CFArrayRef anItemList
)
446 CFMutableArrayRef aResult
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
448 CFIndex anIndex
, aCount
= CFArrayGetCount(anItemList
);
449 for (anIndex
= 0; anIndex
< aCount
; ++anIndex
) {
450 CFDictionaryRef anItem
= CFArrayGetValueAtIndex(anItemList
, anIndex
);
452 CFNumberRef aPID
= CFDictionaryGetValue(anItem
, kPIDKey
);
454 CFArrayAppendValue(aResult
, anItem
);
462 * Append items in anItemList to aDependents which depend on
464 * If anAction is kActionStart, dependent items are those which
465 * require any service provided by aParentItem.
466 * If anAction is kActionStop, dependent items are those which provide
467 * any service required by aParentItem.
469 static void appendDependents(CFMutableArrayRef aDependents
, CFArrayRef anItemList
, CFDictionaryRef aParentItem
, Action anAction
)
471 CFStringRef anInnerKey
, anOuterKey
;
472 CFArrayRef anOuterList
;
474 /* Append the parent item to the list (avoiding duplicates) */
475 if (!CFArrayContainsValue(aDependents
, CFRangeMake(0, CFArrayGetCount(aDependents
)), aParentItem
))
476 CFArrayAppendValue(aDependents
, aParentItem
);
479 * Recursively append any children of the parent item for kStartAction and kStopAction.
480 * Do nothing for other actions.
484 anInnerKey
= kProvidesKey
;
485 anOuterKey
= kRequiresKey
;
488 anInnerKey
= kRequiresKey
;
489 anOuterKey
= kProvidesKey
;
495 anOuterList
= CFDictionaryGetValue(aParentItem
, anOuterKey
);
498 CFIndex anOuterCount
= CFArrayGetCount(anOuterList
);
499 CFIndex anOuterIndex
;
501 for (anOuterIndex
= 0; anOuterIndex
< anOuterCount
; anOuterIndex
++) {
502 CFStringRef anOuterElement
= CFArrayGetValueAtIndex(anOuterList
, anOuterIndex
);
503 CFIndex anItemCount
= CFArrayGetCount(anItemList
);
506 for (anItemIndex
= 0; anItemIndex
< anItemCount
; anItemIndex
++) {
507 CFDictionaryRef anItem
= CFArrayGetValueAtIndex(anItemList
, anItemIndex
);
508 CFArrayRef anInnerList
= CFDictionaryGetValue(anItem
, anInnerKey
);
511 CFArrayContainsValue(anInnerList
, CFRangeMake(0, CFArrayGetCount(anInnerList
)),
513 && !CFArrayContainsValue(aDependents
, CFRangeMake(0, CFArrayGetCount(aDependents
)), anItem
))
514 appendDependents(aDependents
, anItemList
, anItem
, anAction
);
520 CFMutableArrayRef
StartupItemListCreateDependentsList(CFMutableArrayRef anItemList
, CFStringRef aService
, Action anAction
)
522 CFMutableArrayRef aDependents
= NULL
;
523 CFMutableDictionaryRef anItem
= NULL
;
525 if (anItemList
&& aService
)
526 anItem
= StartupItemListGetProvider(anItemList
, aService
);
533 aDependents
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
536 CF_syslog(LOG_EMERG
, CFSTR("Failed to allocate dependancy list for item %@"), anItem
);
539 appendDependents(aDependents
, anItemList
, anItem
, anAction
);
550 * countUnmetRequirements counts the number of items in anItemList
551 * which are pending in aStatusDict.
553 static int countUnmetRequirements(CFDictionaryRef aStatusDict
, CFArrayRef anItemList
)
556 CFIndex anItemCount
= CFArrayGetCount(anItemList
);
559 for (anItemIndex
= 0; anItemIndex
< anItemCount
; anItemIndex
++) {
560 CFStringRef anItem
= CFArrayGetValueAtIndex(anItemList
, anItemIndex
);
561 CFStringRef aStatus
= CFDictionaryGetValue(aStatusDict
, anItem
);
563 if (!aStatus
|| !CFEqual(aStatus
, kRunSuccess
)) {
564 CF_syslog(LOG_DEBUG
, CFSTR("\tFailed requirement/uses: %@"), anItem
);
573 * countDependantsPresent counts the number of items in aWaitingList
574 * which depend on items in anItemList.
576 static int countDependantsPresent(CFArrayRef aWaitingList
, CFArrayRef anItemList
, CFStringRef aKey
)
579 CFIndex anItemCount
= CFArrayGetCount(anItemList
);
582 for (anItemIndex
= 0; anItemIndex
< anItemCount
; anItemIndex
++) {
583 CFStringRef anItem
= CFArrayGetValueAtIndex(anItemList
, anItemIndex
);
584 CFArrayRef aMatchesList
= startupItemListCopyMatches(aWaitingList
, aKey
, anItem
);
587 aCount
= aCount
+ CFArrayGetCount(aMatchesList
);
588 CFRelease(aMatchesList
);
596 * pendingAntecedents returns TRUE if any antecedents of this item
597 * are currently running, have not yet run, or none exist.
600 pendingAntecedents(CFArrayRef aWaitingList
, CFDictionaryRef aStatusDict
, CFArrayRef anAntecedentList
, Action anAction
)
602 int aPendingFlag
= FALSE
;
604 CFIndex anAntecedentCount
= CFArrayGetCount(anAntecedentList
);
605 CFIndex anAntecedentIndex
;
607 for (anAntecedentIndex
= 0; anAntecedentIndex
< anAntecedentCount
; ++anAntecedentIndex
) {
608 CFStringRef anAntecedent
= CFArrayGetValueAtIndex(anAntecedentList
, anAntecedentIndex
);
609 CFStringRef aKey
= (anAction
== kActionStart
) ? kProvidesKey
: kUsesKey
;
610 CFArrayRef aMatchesList
= startupItemListCopyMatches(aWaitingList
, aKey
, anAntecedent
);
613 CFIndex aMatchesListCount
= CFArrayGetCount(aMatchesList
);
614 CFIndex aMatchesListIndex
;
616 for (aMatchesListIndex
= 0; aMatchesListIndex
< aMatchesListCount
; ++aMatchesListIndex
) {
617 CFDictionaryRef anItem
= CFArrayGetValueAtIndex(aMatchesList
, aMatchesListIndex
);
620 !CFDictionaryGetValue(anItem
, kPIDKey
) || !CFDictionaryGetValue(aStatusDict
, anAntecedent
)) {
626 CFRelease(aMatchesList
);
632 return (aPendingFlag
);
636 * checkForDuplicates returns TRUE if an item provides the same service as a
637 * pending item, or an item that already succeeded.
639 static Boolean
checkForDuplicates(CFArrayRef aWaitingList
, CFDictionaryRef aStatusDict
, CFDictionaryRef anItem
)
641 int aDuplicateFlag
= FALSE
;
643 CFArrayRef aProvidesList
= CFDictionaryGetValue(anItem
, kProvidesKey
);
644 CFIndex aProvidesCount
= aProvidesList
? CFArrayGetCount(aProvidesList
) : 0;
645 CFIndex aProvidesIndex
;
647 for (aProvidesIndex
= 0; aProvidesIndex
< aProvidesCount
; ++aProvidesIndex
) {
648 CFStringRef aProvides
= CFArrayGetValueAtIndex(aProvidesList
, aProvidesIndex
);
650 /* If the service succeeded, return true. */
651 CFStringRef aStatus
= CFDictionaryGetValue(aStatusDict
, aProvides
);
652 if (aStatus
&& CFEqual(aStatus
, kRunSuccess
)) {
653 aDuplicateFlag
= TRUE
;
657 * Otherwise test if any item is currently running which
658 * might provide that service.
661 CFArrayRef aMatchesList
= startupItemListCopyMatches(aWaitingList
, kProvidesKey
, aProvides
);
663 CFIndex aMatchesListCount
= CFArrayGetCount(aMatchesList
);
664 CFIndex aMatchesListIndex
;
666 for (aMatchesListIndex
= 0; aMatchesListIndex
< aMatchesListCount
; ++aMatchesListIndex
) {
667 CFDictionaryRef anDupItem
= CFArrayGetValueAtIndex(aMatchesList
, aMatchesListIndex
);
668 if (anDupItem
&& CFDictionaryGetValue(anDupItem
, kPIDKey
)) {
670 * Item is running, avoid
673 aDuplicateFlag
= TRUE
;
676 CFNumberRef anItemDomain
= CFDictionaryGetValue(anItem
, kDomainKey
);
677 CFNumberRef anotherItemDomain
= CFDictionaryGetValue(anDupItem
, kDomainKey
);
679 * If anItem was found later
680 * than aDupItem, stall
681 * anItem until aDupItem
686 CFNumberCompare(anItemDomain
, anotherItemDomain
,
687 NULL
) == kCFCompareGreaterThan
) {
693 aDuplicateFlag
= TRUE
;
699 CFRelease(aMatchesList
);
705 return (aDuplicateFlag
);
708 CFMutableDictionaryRef
StartupItemListGetNext(CFArrayRef aWaitingList
, CFDictionaryRef aStatusDict
, Action anAction
)
710 CFMutableDictionaryRef aNextItem
= NULL
;
711 CFIndex aWaitingCount
= CFArrayGetCount(aWaitingList
);
712 int aMinFailedAntecedents
= INT_MAX
;
713 CFIndex aWaitingIndex
;
726 if (!aWaitingList
|| !aStatusDict
|| aWaitingCount
<= 0)
730 * Iterate through the items in aWaitingList and look for an optimally ready item.
732 for (aWaitingIndex
= 0; aWaitingIndex
< aWaitingCount
; aWaitingIndex
++) {
733 CFMutableDictionaryRef anItem
= (CFMutableDictionaryRef
) CFArrayGetValueAtIndex(aWaitingList
, aWaitingIndex
);
734 CFArrayRef anAntecedentList
;
735 int aFailedAntecedentsCount
= 0; /* Number of unmet soft
737 Boolean aBestPick
= FALSE
; /* Is this the best pick
740 /* Filter out running items. */
741 if (CFDictionaryGetValue(anItem
, kPIDKey
))
745 * Filter out dupilicate services; if someone has
746 * provided what we provide, we don't run.
748 if (checkForDuplicates(aWaitingList
, aStatusDict
, anItem
)) {
749 CF_syslog(LOG_DEBUG
, CFSTR("Skipping %@ because of duplicate service."),
750 CFDictionaryGetValue(anItem
, kDescriptionKey
));
754 * Dependencies don't matter when restarting an item;
757 if (anAction
== kActionRestart
) {
761 anAntecedentList
= CFDictionaryGetValue(anItem
, ((anAction
== kActionStart
) ? kRequiresKey
: kProvidesKey
));
763 CF_syslog(LOG_DEBUG
, CFSTR("Checking %@"), CFDictionaryGetValue(anItem
, kDescriptionKey
));
765 if (anAntecedentList
)
766 CF_syslog(LOG_DEBUG
, CFSTR("Antecedents: %@"), anAntecedentList
);
768 syslog(LOG_DEBUG
, "No antecedents");
771 * Filter out the items which have unsatisfied antecedents.
773 if (anAntecedentList
&&
774 ((anAction
== kActionStart
) ?
775 countUnmetRequirements(aStatusDict
, anAntecedentList
) :
776 countDependantsPresent(aWaitingList
, anAntecedentList
, kRequiresKey
)))
780 * anItem has all hard dependancies met; check for soft dependancies.
781 * We'll favor the item with the fewest unmet soft dependancies here.
783 anAntecedentList
= CFDictionaryGetValue(anItem
, ((anAction
== kActionStart
) ? kUsesKey
: kProvidesKey
));
785 if (anAntecedentList
)
786 CF_syslog(LOG_DEBUG
, CFSTR("Soft dependancies: %@"), anAntecedentList
);
788 syslog(LOG_DEBUG
, "No soft dependancies");
790 if (anAntecedentList
) {
791 aFailedAntecedentsCount
=
792 ((anAction
== kActionStart
) ?
793 countUnmetRequirements(aStatusDict
, anAntecedentList
) :
794 countDependantsPresent(aWaitingList
, anAntecedentList
, kUsesKey
));
796 if (aMinFailedAntecedents
> 0)
801 * anItem has unmet dependencies that will
802 * likely be met in the future, so delay it
804 if (aFailedAntecedentsCount
> 0 && pendingAntecedents(aWaitingList
, aStatusDict
, anAntecedentList
, anAction
)) {
807 if (aFailedAntecedentsCount
> 0)
808 syslog(LOG_DEBUG
, "Total: %d", aFailedAntecedentsCount
);
810 if (aFailedAntecedentsCount
> aMinFailedAntecedents
)
811 continue; /* Another item already won out */
813 if (aFailedAntecedentsCount
< aMinFailedAntecedents
)
820 * anItem has less unmet
821 * dependancies than any
822 * other item so far, so it
825 syslog(LOG_DEBUG
, "Best pick so far, based on failed dependancies (%d->%d)",
826 aMinFailedAntecedents
, aFailedAntecedentsCount
);
829 * We have a winner! Update success
830 * parameters to match anItem.
832 aMinFailedAntecedents
= aFailedAntecedentsCount
;
835 } /* End of waiting list loop. */
840 CFStringRef
StartupItemGetDescription(CFMutableDictionaryRef anItem
)
842 CFStringRef aString
= NULL
;
845 aString
= CFDictionaryGetValue(anItem
, kDescriptionKey
);
851 pid_t
StartupItemGetPID(CFDictionaryRef anItem
)
853 CFIndex anItemPID
= 0;
854 CFNumberRef aPIDNumber
= anItem
? CFDictionaryGetValue(anItem
, kPIDKey
) : NULL
;
855 if (aPIDNumber
&& CFNumberGetValue(aPIDNumber
, kCFNumberCFIndexType
, &anItemPID
))
856 return (pid_t
) anItemPID
;
861 CFMutableDictionaryRef
StartupItemWithPID(CFArrayRef anItemList
, pid_t aPID
)
863 CFIndex anItemCount
= CFArrayGetCount(anItemList
);
866 for (anItemIndex
= 0; anItemIndex
< anItemCount
; anItemIndex
++) {
867 CFMutableDictionaryRef anItem
= (CFMutableDictionaryRef
) CFArrayGetValueAtIndex(anItemList
, anItemIndex
);
868 CFNumberRef aPIDNumber
= CFDictionaryGetValue(anItem
, kPIDKey
);
872 CFNumberGetValue(aPIDNumber
, kCFNumberCFIndexType
, &anItemPID
);
874 if ((pid_t
) anItemPID
== aPID
)
882 int StartupItemRun(CFMutableDictionaryRef aStatusDict
, CFMutableDictionaryRef anItem
, Action anAction
)
885 CFArrayRef aProvidesList
= CFDictionaryGetValue(anItem
, kProvidesKey
);
886 static const CFStringRef stubitems
[] = {
887 CFSTR("BootROMUpdater"), /* 3893064 */
888 CFSTR("FCUUpdater"), /* 3893064 */
889 CFSTR("AutoProtect Daemon"), /* 3965785 */
890 CFSTR("Check For Missed Tasks"), /* 3965785 */
891 CFSTR("Privacy"), /* 3933484 */
892 CFSTR("Firmware Update Checking"), /* 4001504 */
894 CFSTR("M-Audio FireWire Audio Support"), /* 3931757 */
895 CFSTR("help for M-Audio Delta Family"), /* 3931757 */
896 CFSTR("help for M-Audio Devices"), /* 3931757 */
897 CFSTR("help for M-Audio Revo 5.1"), /* 3931757 */
898 CFSTR("M-Audio USB Duo Configuration Service"), /* 3931757 */
899 CFSTR("firmware loader for M-Audio devices"), /* 3931757 */
900 CFSTR("M-Audio MobilePre USB Configuration Service"), /* 3931757 */
901 CFSTR("M-Audio OmniStudio USB Configuration Service"), /* 3931757 */
902 CFSTR("M-Audio Transit USB Configuration Service"), /* 3931757 */
903 CFSTR("M-Audio Audiophile USB Configuration Service"), /* 3931757 */
906 const CFStringRef
*c
;
908 if (aProvidesList
&& anAction
== kActionStop
) {
909 CFIndex aProvidesCount
= CFArrayGetCount(aProvidesList
);
910 for (c
= stubitems
; *c
; c
++) {
911 if (CFArrayContainsValue(aProvidesList
, CFRangeMake(0, aProvidesCount
), *c
)) {
913 CFNumberRef aProcessNumber
= CFNumberCreate(NULL
, kCFNumberCFIndexType
, &aPID
);
915 CFDictionarySetValue(anItem
, kPIDKey
, aProcessNumber
);
916 CFRelease(aProcessNumber
);
918 StartupItemExit(aStatusDict
, anItem
, TRUE
);
924 if (anAction
== kActionNone
) {
925 StartupItemExit(aStatusDict
, anItem
, TRUE
);
928 CFStringRef aBundlePathString
= CFDictionaryGetValue(anItem
, kBundlePathKey
);
929 char aBundlePath
[PATH_MAX
];
930 char anExecutable
[PATH_MAX
];
933 if (!CFStringGetCString(aBundlePathString
, aBundlePath
, sizeof(aBundlePath
), kCFStringEncodingUTF8
)) {
934 CF_syslog(LOG_EMERG
, CFSTR("Internal error while running item %@"), aBundlePathString
);
937 /* Compute path to excecutable */
938 tmp
= rindex(aBundlePath
, '/');
939 snprintf(anExecutable
, sizeof(anExecutable
), "%s%s", aBundlePath
, tmp
);
945 if (access(anExecutable
, X_OK
)) {
947 * Add PID key so that this item is marked as having
951 CFNumberRef aProcessNumber
= CFNumberCreate(NULL
, kCFNumberCFIndexType
, &aPID
);
953 CFDictionarySetValue(anItem
, kPIDKey
, aProcessNumber
);
954 CFRelease(aProcessNumber
);
956 CFDictionarySetValue(anItem
, kErrorKey
, kErrorPermissions
);
957 StartupItemExit(aStatusDict
, anItem
, FALSE
);
958 syslog(LOG_ERR
, "No executable file %s", anExecutable
);
960 pid_t aProccessID
= fork();
962 switch (aProccessID
) {
963 case -1: /* SystemStarter (fork failed) */
964 CFDictionarySetValue(anItem
, kErrorKey
, kErrorFork
);
965 StartupItemExit(aStatusDict
, anItem
, FALSE
);
967 CF_syslog(LOG_ERR
, CFSTR("Failed to fork for item %@: %s"), aBundlePathString
, strerror(errno
));
971 default: /* SystemStarter (fork succeeded) */
973 CFIndex aPID
= (CFIndex
) aProccessID
;
974 CFNumberRef aProcessNumber
= CFNumberCreate(NULL
, kCFNumberCFIndexType
, &aPID
);
976 CFDictionarySetValue(anItem
, kPIDKey
, aProcessNumber
);
977 CFRelease(aProcessNumber
);
979 syslog(LOG_DEBUG
, "Running command (%d): %s %s",
980 aProccessID
, anExecutable
, argumentForAction(anAction
));
988 syslog(LOG_WARNING
, "Unable to create session for item %s: %m", anExecutable
);
990 anError
= execl(anExecutable
, anExecutable
, argumentForAction(anAction
), NULL
);
992 /* We shouldn't get here. */
994 syslog(LOG_ERR
, "execl(\"%s\"): %m", anExecutable
);
1006 StartupItemSetStatus(CFMutableDictionaryRef aStatusDict
, CFMutableDictionaryRef anItem
, CFStringRef aServiceName
,
1007 Boolean aSuccess
, Boolean aReplaceFlag
)
1009 void (*anAction
) (CFMutableDictionaryRef
, const void *, const void *) = aReplaceFlag
?
1010 CFDictionarySetValue
: CFDictionaryAddValue
;
1012 if (aStatusDict
&& anItem
) {
1013 CFArrayRef aProvidesList
= CFDictionaryGetValue(anItem
, kProvidesKey
);
1014 if (aProvidesList
) {
1015 CFIndex aProvidesCount
= CFArrayGetCount(aProvidesList
);
1016 CFIndex aProvidesIndex
;
1019 * If a service name was specified, and it is valid,
1022 if (aServiceName
&& CFArrayContainsValue(aProvidesList
, CFRangeMake(0, aProvidesCount
), aServiceName
)) {
1023 aProvidesList
= CFArrayCreate(NULL
, (const void **)&aServiceName
, 1, &kCFTypeArrayCallBacks
);
1026 CFRetain(aProvidesList
);
1029 for (aProvidesIndex
= 0; aProvidesIndex
< aProvidesCount
; aProvidesIndex
++) {
1030 CFStringRef aService
= CFArrayGetValueAtIndex(aProvidesList
, aProvidesIndex
);
1033 anAction(aStatusDict
, aService
, kRunSuccess
);
1035 anAction(aStatusDict
, aService
, kRunFailure
);
1038 CFRelease(aProvidesList
);
1043 void StartupItemExit(CFMutableDictionaryRef aStatusDict
, CFMutableDictionaryRef anItem
, Boolean aSuccess
)
1045 StartupItemSetStatus(aStatusDict
, anItem
, NULL
, aSuccess
, FALSE
);