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>
38 #include <CoreFoundation/CoreFoundation.h>
39 #include "StartupItems.h"
41 #define kStartupItemsPath "/StartupItems"
42 #define kParametersFile "StartupParameters.plist"
43 #define kDisabledFile ".disabled"
45 #define kRunSuccess CFSTR("success")
46 #define kRunFailure CFSTR("failure")
48 static const char *argumentForAction(Action anAction
)
62 #define checkTypeOfValue(aKey,aTypeID) \
64 CFStringRef aProperty = CFDictionaryGetValue(aConfig, aKey); \
65 if (aProperty && CFGetTypeID(aProperty) != aTypeID) \
69 static int StartupItemValidate(CFDictionaryRef aConfig
)
71 if (aConfig
&& CFGetTypeID(aConfig
) == CFDictionaryGetTypeID()) {
72 checkTypeOfValue(kProvidesKey
, CFArrayGetTypeID());
73 checkTypeOfValue(kRequiresKey
, CFArrayGetTypeID());
81 * remove item from waiting list
83 void RemoveItemFromWaitingList(StartupContext aStartupContext
, CFMutableDictionaryRef anItem
)
85 /* Remove the item from the waiting list. */
86 if (aStartupContext
&& anItem
&& aStartupContext
->aWaitingList
) {
87 CFRange aRange
= { 0, CFArrayGetCount(aStartupContext
->aWaitingList
) };
88 CFIndex anIndex
= CFArrayGetFirstIndexOfValue(aStartupContext
->aWaitingList
, aRange
, anItem
);
91 CFArrayRemoveValueAtIndex(aStartupContext
->aWaitingList
, anIndex
);
97 * add item to failed list, create list if it doesn't exist
98 * return and fail quietly if it can't create list
100 void AddItemToFailedList(StartupContext aStartupContext
, CFMutableDictionaryRef anItem
)
102 if (aStartupContext
&& anItem
) {
103 /* create the failed list if it doesn't exist */
104 if (!aStartupContext
->aFailedList
) {
105 aStartupContext
->aFailedList
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
107 if (aStartupContext
->aFailedList
) {
108 CFArrayAppendValue(aStartupContext
->aFailedList
, anItem
);
114 * startupItemListGetMatches returns an array of items which contain the string aService in the key aKey
116 static CFMutableArrayRef
startupItemListGetMatches(CFArrayRef anItemList
, CFStringRef aKey
, CFStringRef aService
)
118 CFMutableArrayRef aResult
= NULL
;
120 if (anItemList
&& aKey
&& aService
) {
121 CFIndex anItemCount
= CFArrayGetCount(anItemList
);
122 CFIndex anItemIndex
= 0;
124 aResult
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
126 for (anItemIndex
= 0; anItemIndex
< anItemCount
; ++anItemIndex
) {
127 CFMutableDictionaryRef anItem
= (CFMutableDictionaryRef
) CFArrayGetValueAtIndex(anItemList
, anItemIndex
);
128 CFArrayRef aList
= CFDictionaryGetValue(anItem
, aKey
);
131 if (CFArrayContainsValue(aList
, CFRangeMake(0, CFArrayGetCount(aList
)), aService
) &&
132 !CFArrayContainsValue(aResult
, CFRangeMake(0, CFArrayGetCount(aResult
)), anItem
)) {
133 CFArrayAppendValue(aResult
, anItem
);
141 static void SpecialCasesStartupItemHandler(CFMutableDictionaryRef aConfig
)
143 static const CFStringRef stubitems
[] = {
145 CFSTR("System Tuning"),
146 CFSTR("SecurityServer"),
152 CFSTR("NetworkExtensions"),
153 CFSTR("DirectoryServices"),
154 CFSTR("Network Configuration"),
155 CFSTR("mDNSResponder"),
157 CFSTR("Core Graphics"),
158 CFSTR("Core Services"),
163 CFMutableArrayRef aList
, aNewList
;
165 CFStringRef ci
, type
= kRequiresKey
;
166 const CFStringRef
*c
;
169 aList
= (CFMutableArrayRef
) CFDictionaryGetValue(aConfig
, type
);
171 aCount
= CFArrayGetCount(aList
);
173 aNewList
= CFArrayCreateMutable(kCFAllocatorDefault
, aCount
, &kCFTypeArrayCallBacks
);
175 for (i
= 0; i
< aCount
; i
++) {
176 ci
= CFArrayGetValueAtIndex(aList
, i
);
177 CF_syslog(LOG_DEBUG
, CFSTR("%@: Evaluating %@"), type
, ci
);
178 for (c
= stubitems
; *c
; c
++) {
184 CFArrayAppendValue(aNewList
, ci
);
185 CF_syslog(LOG_DEBUG
, CFSTR("%@: Keeping %@"), type
, ci
);
189 CFDictionaryReplaceValue(aConfig
, type
, aNewList
);
191 if (type
== kUsesKey
)
197 CFIndex
StartupItemListCountServices(CFArrayRef anItemList
)
202 CFIndex anItemCount
= CFArrayGetCount(anItemList
);
203 CFIndex anItemIndex
= 0;
205 for (anItemIndex
= 0; anItemIndex
< anItemCount
; ++anItemIndex
) {
206 CFDictionaryRef anItem
= CFArrayGetValueAtIndex(anItemList
, anItemIndex
);
207 CFArrayRef aProvidesList
= CFDictionaryGetValue(anItem
, kProvidesKey
);
210 aResult
+= CFArrayGetCount(aProvidesList
);
216 static bool StartupItemSecurityCheck(const char *aPath
)
218 struct stat aStatBuf
;
221 /* should use lstatx_np() on Tiger? */
222 if (lstat(aPath
, &aStatBuf
) == -1) {
224 syslog(LOG_ERR
, "lstat(\"%s\"): %m", aPath
);
227 if (!(S_ISREG(aStatBuf
.st_mode
) || S_ISDIR(aStatBuf
.st_mode
))) {
228 syslog(LOG_WARNING
, "\"%s\" failed security check: not a directory or regular file", aPath
);
231 if ((aStatBuf
.st_mode
& ALLPERMS
) & ~(S_IRWXU
| S_IRGRP
| S_IXGRP
| S_IROTH
| S_IXOTH
)) {
232 syslog(LOG_WARNING
, "\"%s\" failed security check: permissions", aPath
);
235 if (aStatBuf
.st_uid
!= 0) {
236 syslog(LOG_WARNING
, "\"%s\" failed security check: not owned by UID 0", aPath
);
239 if (aStatBuf
.st_gid
!= 0) {
240 syslog(LOG_WARNING
, "\"%s\" failed security check: not owned by GID 0", aPath
);
244 mkdir(kFixerDir
, ACCESSPERMS
);
245 close(open(kFixerPath
, O_RDWR
|O_CREAT
|O_NOCTTY
, DEFFILEMODE
));
250 CFMutableArrayRef
StartupItemListCreateWithMask(NSSearchPathDomainMask aMask
)
252 CFMutableArrayRef anItemList
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
254 char aPath
[PATH_MAX
];
255 CFIndex aDomainIndex
= 0;
257 NSSearchPathEnumerationState aState
= NSStartSearchPathEnumeration(NSLibraryDirectory
, aMask
);
259 while ((aState
= NSGetNextSearchPathEnumeration(aState
, aPath
))) {
262 strcpy(aPath
+ strlen(aPath
), kStartupItemsPath
);
265 if (!StartupItemSecurityCheck(aPath
))
268 if ((aDirectory
= opendir(aPath
))) {
269 struct dirent
*aBundle
;
271 while ((aBundle
= readdir(aDirectory
))) {
272 struct stat aStatBuf
;
273 char *aBundleName
= aBundle
->d_name
;
274 char aBundlePath
[PATH_MAX
];
275 char aBundleExecutablePath
[PATH_MAX
];
276 char aConfigFile
[PATH_MAX
];
277 char aDisabledFile
[PATH_MAX
];
279 if (aBundleName
[0] == '.')
282 syslog(LOG_DEBUG
, "Found item: %s", aBundleName
);
284 sprintf(aBundlePath
, "%s/%s", aPath
, aBundleName
);
285 sprintf(aBundleExecutablePath
, "%s/%s", aBundlePath
, aBundleName
);
286 sprintf(aConfigFile
, "%s/%s", aBundlePath
, kParametersFile
);
287 sprintf(aDisabledFile
, "%s/%s", aBundlePath
, kDisabledFile
);
289 if (lstat(aDisabledFile
, &aStatBuf
) == 0) {
290 syslog(LOG_NOTICE
, "Skipping disabled StartupItem: %s", aBundlePath
);
293 if (!StartupItemSecurityCheck(aBundlePath
))
295 if (!StartupItemSecurityCheck(aBundleExecutablePath
))
297 if (!StartupItemSecurityCheck(aConfigFile
))
300 /* Stow away the plist data for each bundle */
302 int aConfigFileDescriptor
;
304 if ((aConfigFileDescriptor
= open(aConfigFile
, O_RDONLY
|O_NOCTTY
, (mode_t
) 0)) != -1) {
305 struct stat aConfigFileStatBuffer
;
307 if (stat(aConfigFile
, &aConfigFileStatBuffer
) != -1) {
308 off_t aConfigFileContentsSize
= aConfigFileStatBuffer
.st_size
;
309 char *aConfigFileContentsBuffer
;
311 if ((aConfigFileContentsBuffer
=
312 mmap((caddr_t
) 0, aConfigFileContentsSize
,
313 PROT_READ
, MAP_FILE
| MAP_PRIVATE
,
314 aConfigFileDescriptor
, (off_t
) 0)) != (caddr_t
) - 1) {
315 CFDataRef aConfigData
= NULL
;
316 CFMutableDictionaryRef aConfig
= NULL
;
319 CFDataCreateWithBytesNoCopy(NULL
,
320 (const UInt8
*)aConfigFileContentsBuffer
,
321 aConfigFileContentsSize
,
325 aConfig
= (CFMutableDictionaryRef
)
326 CFPropertyListCreateFromXMLData(NULL
, aConfigData
,
327 kCFPropertyListMutableContainers
,
330 if (StartupItemValidate(aConfig
)) {
331 CFStringRef aBundlePathString
=
332 CFStringCreateWithCString(NULL
, aBundlePath
,
333 kCFStringEncodingUTF8
);
335 CFNumberRef aDomainNumber
=
336 CFNumberCreate(NULL
, kCFNumberCFIndexType
,
339 CFDictionarySetValue(aConfig
, kBundlePathKey
,
341 CFDictionarySetValue(aConfig
, kDomainKey
, aDomainNumber
);
342 CFRelease(aDomainNumber
);
343 SpecialCasesStartupItemHandler(aConfig
);
344 CFArrayAppendValue(anItemList
, aConfig
);
346 CFRelease(aBundlePathString
);
348 syslog(LOG_ERR
, "Malformatted parameters file: %s",
355 CFRelease(aConfigData
);
357 if (munmap(aConfigFileContentsBuffer
, aConfigFileContentsSize
) ==
360 "Unable to unmap parameters file %s for item %s: %m",
361 aConfigFile
, aBundleName
);
365 "Unable to map parameters file %s for item %s: %m",
366 aConfigFile
, aBundleName
);
369 syslog(LOG_ERR
, "Unable to stat parameters file %s for item %s: %m",
370 aConfigFile
, aBundleName
);
373 if (close(aConfigFileDescriptor
) == -1) {
374 syslog(LOG_ERR
, "Unable to close parameters file %s for item %s: %m",
375 aConfigFile
, aBundleName
);
378 syslog(LOG_ERR
, "Unable to open parameters file %s for item %s: %m", aConfigFile
,
383 if (closedir(aDirectory
) == -1) {
384 syslog(LOG_WARNING
, "Unable to directory bundle %s: %m", aPath
);
387 if (errno
!= ENOENT
) {
388 syslog(LOG_WARNING
, "Open on directory %s failed: %m", aPath
);
397 CFMutableDictionaryRef
StartupItemListGetProvider(CFArrayRef anItemList
, CFStringRef aService
)
399 CFMutableDictionaryRef aResult
= NULL
;
400 CFMutableArrayRef aList
= startupItemListGetMatches(anItemList
, kProvidesKey
, aService
);
402 if (aList
&& CFArrayGetCount(aList
) > 0)
403 aResult
= (CFMutableDictionaryRef
) CFArrayGetValueAtIndex(aList
, 0);
408 CFArrayRef
StartupItemListGetRunning(CFArrayRef anItemList
)
410 CFMutableArrayRef aResult
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
412 CFIndex anIndex
, aCount
= CFArrayGetCount(anItemList
);
413 for (anIndex
= 0; anIndex
< aCount
; ++anIndex
) {
414 CFDictionaryRef anItem
= CFArrayGetValueAtIndex(anItemList
, anIndex
);
416 CFNumberRef aPID
= CFDictionaryGetValue(anItem
, kPIDKey
);
418 CFArrayAppendValue(aResult
, anItem
);
426 * Append items in anItemList to aDependents which depend on
428 * If anAction is kActionStart, dependent items are those which
429 * require any service provided by aParentItem.
430 * If anAction is kActionStop, dependent items are those which provide
431 * any service required by aParentItem.
433 static void appendDependents(CFMutableArrayRef aDependents
, CFArrayRef anItemList
, CFDictionaryRef aParentItem
, Action anAction
)
435 CFStringRef anInnerKey
, anOuterKey
;
436 CFArrayRef anOuterList
;
438 /* Append the parent item to the list (avoiding duplicates) */
439 if (!CFArrayContainsValue(aDependents
, CFRangeMake(0, CFArrayGetCount(aDependents
)), aParentItem
))
440 CFArrayAppendValue(aDependents
, aParentItem
);
443 * Recursively append any children of the parent item for kStartAction and kStopAction.
444 * Do nothing for other actions.
448 anInnerKey
= kProvidesKey
;
449 anOuterKey
= kRequiresKey
;
452 anInnerKey
= kRequiresKey
;
453 anOuterKey
= kProvidesKey
;
459 anOuterList
= CFDictionaryGetValue(aParentItem
, anOuterKey
);
462 CFIndex anOuterCount
= CFArrayGetCount(anOuterList
);
463 CFIndex anOuterIndex
;
465 for (anOuterIndex
= 0; anOuterIndex
< anOuterCount
; anOuterIndex
++) {
466 CFStringRef anOuterElement
= CFArrayGetValueAtIndex(anOuterList
, anOuterIndex
);
467 CFIndex anItemCount
= CFArrayGetCount(anItemList
);
470 for (anItemIndex
= 0; anItemIndex
< anItemCount
; anItemIndex
++) {
471 CFDictionaryRef anItem
= CFArrayGetValueAtIndex(anItemList
, anItemIndex
);
472 CFArrayRef anInnerList
= CFDictionaryGetValue(anItem
, anInnerKey
);
475 CFArrayContainsValue(anInnerList
, CFRangeMake(0, CFArrayGetCount(anInnerList
)),
477 && !CFArrayContainsValue(aDependents
, CFRangeMake(0, CFArrayGetCount(aDependents
)), anItem
))
478 appendDependents(aDependents
, anItemList
, anItem
, anAction
);
484 CFMutableArrayRef
StartupItemListCreateDependentsList(CFMutableArrayRef anItemList
, CFStringRef aService
, Action anAction
)
486 CFMutableArrayRef aDependents
= NULL
;
487 CFMutableDictionaryRef anItem
= NULL
;
489 if (anItemList
&& aService
)
490 anItem
= StartupItemListGetProvider(anItemList
, aService
);
497 aDependents
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
500 CF_syslog(LOG_EMERG
, CFSTR("Failed to allocate dependancy list for item %@"), anItem
);
503 appendDependents(aDependents
, anItemList
, anItem
, anAction
);
514 * countUnmetRequirements counts the number of items in anItemList
515 * which are pending in aStatusDict.
517 static int countUnmetRequirements(CFDictionaryRef aStatusDict
, CFArrayRef anItemList
)
520 CFIndex anItemCount
= CFArrayGetCount(anItemList
);
523 for (anItemIndex
= 0; anItemIndex
< anItemCount
; anItemIndex
++) {
524 CFStringRef anItem
= CFArrayGetValueAtIndex(anItemList
, anItemIndex
);
525 CFStringRef aStatus
= CFDictionaryGetValue(aStatusDict
, anItem
);
527 if (!aStatus
|| !CFEqual(aStatus
, kRunSuccess
)) {
528 CF_syslog(LOG_DEBUG
, CFSTR("\tFailed requirement/uses: %@"), anItem
);
537 * countDependantsPresent counts the number of items in aWaitingList
538 * which depend on items in anItemList.
540 static int countDependantsPresent(CFArrayRef aWaitingList
, CFArrayRef anItemList
, CFStringRef aKey
)
543 CFIndex anItemCount
= CFArrayGetCount(anItemList
);
546 for (anItemIndex
= 0; anItemIndex
< anItemCount
; anItemIndex
++) {
547 CFStringRef anItem
= CFArrayGetValueAtIndex(anItemList
, anItemIndex
);
548 CFArrayRef aMatchesList
= startupItemListGetMatches(aWaitingList
, aKey
, anItem
);
551 aCount
= aCount
+ CFArrayGetCount(aMatchesList
);
552 CFRelease(aMatchesList
);
560 * pendingAntecedents returns TRUE if any antecedents of this item
561 * are currently running, have not yet run, or none exist.
564 pendingAntecedents(CFArrayRef aWaitingList
, CFDictionaryRef aStatusDict
, CFArrayRef anAntecedentList
, Action anAction
)
566 int aPendingFlag
= FALSE
;
568 CFIndex anAntecedentCount
= CFArrayGetCount(anAntecedentList
);
569 CFIndex anAntecedentIndex
;
571 for (anAntecedentIndex
= 0; anAntecedentIndex
< anAntecedentCount
; ++anAntecedentIndex
) {
572 CFStringRef anAntecedent
= CFArrayGetValueAtIndex(anAntecedentList
, anAntecedentIndex
);
573 CFStringRef aKey
= (anAction
== kActionStart
) ? kProvidesKey
: kUsesKey
;
574 CFArrayRef aMatchesList
= startupItemListGetMatches(aWaitingList
, aKey
, anAntecedent
);
577 CFIndex aMatchesListCount
= CFArrayGetCount(aMatchesList
);
578 CFIndex aMatchesListIndex
;
580 for (aMatchesListIndex
= 0; aMatchesListIndex
< aMatchesListCount
; ++aMatchesListIndex
) {
581 CFDictionaryRef anItem
= CFArrayGetValueAtIndex(aMatchesList
, aMatchesListIndex
);
584 !CFDictionaryGetValue(anItem
, kPIDKey
) || !CFDictionaryGetValue(aStatusDict
, anAntecedent
)) {
590 CFRelease(aMatchesList
);
596 return (aPendingFlag
);
600 * checkForDuplicates returns TRUE if an item provides the same service as a
601 * pending item, or an item that already succeeded.
603 static Boolean
checkForDuplicates(CFArrayRef aWaitingList
, CFDictionaryRef aStatusDict
, CFDictionaryRef anItem
)
605 int aDuplicateFlag
= FALSE
;
607 CFArrayRef aProvidesList
= CFDictionaryGetValue(anItem
, kProvidesKey
);
608 CFIndex aProvidesCount
= aProvidesList
? CFArrayGetCount(aProvidesList
) : 0;
609 CFIndex aProvidesIndex
;
611 for (aProvidesIndex
= 0; aProvidesIndex
< aProvidesCount
; ++aProvidesIndex
) {
612 CFStringRef aProvides
= CFArrayGetValueAtIndex(aProvidesList
, aProvidesIndex
);
614 /* If the service succeeded, return true. */
615 CFStringRef aStatus
= CFDictionaryGetValue(aStatusDict
, aProvides
);
616 if (aStatus
&& CFEqual(aStatus
, kRunSuccess
)) {
617 aDuplicateFlag
= TRUE
;
621 * Otherwise test if any item is currently running which
622 * might provide that service.
625 CFArrayRef aMatchesList
= startupItemListGetMatches(aWaitingList
, kProvidesKey
, aProvides
);
627 CFIndex aMatchesListCount
= CFArrayGetCount(aMatchesList
);
628 CFIndex aMatchesListIndex
;
630 for (aMatchesListIndex
= 0; aMatchesListIndex
< aMatchesListCount
; ++aMatchesListIndex
) {
631 CFDictionaryRef anDupItem
= CFArrayGetValueAtIndex(aMatchesList
, aMatchesListIndex
);
632 if (anDupItem
&& CFDictionaryGetValue(anDupItem
, kPIDKey
)) {
634 * Item is running, avoid
637 aDuplicateFlag
= TRUE
;
640 CFNumberRef anItemDomain
= CFDictionaryGetValue(anItem
, kDomainKey
);
641 CFNumberRef anotherItemDomain
= CFDictionaryGetValue(anDupItem
, kDomainKey
);
643 * If anItem was found later
644 * than aDupItem, stall
645 * anItem until aDupItem
650 CFNumberCompare(anItemDomain
, anotherItemDomain
,
651 NULL
) == kCFCompareGreaterThan
) {
657 aDuplicateFlag
= TRUE
;
663 CFRelease(aMatchesList
);
669 return (aDuplicateFlag
);
672 CFMutableDictionaryRef
StartupItemListGetNext(CFArrayRef aWaitingList
, CFDictionaryRef aStatusDict
, Action anAction
)
674 CFMutableDictionaryRef aNextItem
= NULL
;
675 CFIndex aWaitingCount
= CFArrayGetCount(aWaitingList
);
676 int aMinFailedAntecedents
= INT_MAX
;
677 CFIndex aWaitingIndex
;
690 if (!aWaitingList
|| !aStatusDict
|| aWaitingCount
<= 0)
694 * Iterate through the items in aWaitingList and look for an optimally ready item.
696 for (aWaitingIndex
= 0; aWaitingIndex
< aWaitingCount
; aWaitingIndex
++) {
697 CFMutableDictionaryRef anItem
= (CFMutableDictionaryRef
) CFArrayGetValueAtIndex(aWaitingList
, aWaitingIndex
);
698 CFArrayRef anAntecedentList
;
699 int aFailedAntecedentsCount
= 0; /* Number of unmet soft
701 Boolean aBestPick
= FALSE
; /* Is this the best pick
704 /* Filter out running items. */
705 if (CFDictionaryGetValue(anItem
, kPIDKey
))
709 * Filter out dupilicate services; if someone has
710 * provided what we provide, we don't run.
712 if (checkForDuplicates(aWaitingList
, aStatusDict
, anItem
)) {
713 CF_syslog(LOG_DEBUG
, CFSTR("Skipping %@ because of duplicate service."),
714 CFDictionaryGetValue(anItem
, kDescriptionKey
));
718 * Dependencies don't matter when restarting an item;
721 if (anAction
== kActionRestart
) {
725 anAntecedentList
= CFDictionaryGetValue(anItem
, ((anAction
== kActionStart
) ? kRequiresKey
: kProvidesKey
));
727 CF_syslog(LOG_DEBUG
, CFSTR("Checking %@"), CFDictionaryGetValue(anItem
, kDescriptionKey
));
729 if (anAntecedentList
)
730 CF_syslog(LOG_DEBUG
, CFSTR("Antecedents: %@"), anAntecedentList
);
732 syslog(LOG_DEBUG
, "No antecedents");
735 * Filter out the items which have unsatisfied antecedents.
737 if (anAntecedentList
&&
738 ((anAction
== kActionStart
) ?
739 countUnmetRequirements(aStatusDict
, anAntecedentList
) :
740 countDependantsPresent(aWaitingList
, anAntecedentList
, kRequiresKey
)))
744 * anItem has all hard dependancies met; check for soft dependancies.
745 * We'll favor the item with the fewest unmet soft dependancies here.
747 anAntecedentList
= CFDictionaryGetValue(anItem
, ((anAction
== kActionStart
) ? kUsesKey
: kProvidesKey
));
749 if (anAntecedentList
)
750 CF_syslog(LOG_DEBUG
, CFSTR("Soft dependancies: %@"), anAntecedentList
);
752 syslog(LOG_DEBUG
, "No soft dependancies");
754 if (anAntecedentList
) {
755 aFailedAntecedentsCount
=
756 ((anAction
== kActionStart
) ?
757 countUnmetRequirements(aStatusDict
, anAntecedentList
) :
758 countDependantsPresent(aWaitingList
, anAntecedentList
, kUsesKey
));
760 if (aMinFailedAntecedents
> 0)
765 * anItem has unmet dependencies that will
766 * likely be met in the future, so delay it
768 if (aFailedAntecedentsCount
> 0 && pendingAntecedents(aWaitingList
, aStatusDict
, anAntecedentList
, anAction
)) {
771 if (aFailedAntecedentsCount
> 0)
772 syslog(LOG_DEBUG
, "Total: %d", aFailedAntecedentsCount
);
774 if (aFailedAntecedentsCount
> aMinFailedAntecedents
)
775 continue; /* Another item already won out */
777 if (aFailedAntecedentsCount
< aMinFailedAntecedents
)
784 * anItem has less unmet
785 * dependancies than any
786 * other item so far, so it
789 syslog(LOG_DEBUG
, "Best pick so far, based on failed dependancies (%d->%d)",
790 aMinFailedAntecedents
, aFailedAntecedentsCount
);
793 * We have a winner! Update success
794 * parameters to match anItem.
796 aMinFailedAntecedents
= aFailedAntecedentsCount
;
799 } /* End of waiting list loop. */
804 CFStringRef
StartupItemGetDescription(CFMutableDictionaryRef anItem
)
806 CFStringRef aString
= NULL
;
809 aString
= CFDictionaryGetValue(anItem
, kDescriptionKey
);
815 pid_t
StartupItemGetPID(CFDictionaryRef anItem
)
817 CFIndex anItemPID
= 0;
818 CFNumberRef aPIDNumber
= anItem
? CFDictionaryGetValue(anItem
, kPIDKey
) : NULL
;
819 if (aPIDNumber
&& CFNumberGetValue(aPIDNumber
, kCFNumberCFIndexType
, &anItemPID
))
820 return (pid_t
) anItemPID
;
825 CFMutableDictionaryRef
StartupItemWithPID(CFArrayRef anItemList
, pid_t aPID
)
827 CFIndex anItemCount
= CFArrayGetCount(anItemList
);
830 for (anItemIndex
= 0; anItemIndex
< anItemCount
; anItemIndex
++) {
831 CFMutableDictionaryRef anItem
= (CFMutableDictionaryRef
) CFArrayGetValueAtIndex(anItemList
, anItemIndex
);
832 CFNumberRef aPIDNumber
= CFDictionaryGetValue(anItem
, kPIDKey
);
836 CFNumberGetValue(aPIDNumber
, kCFNumberCFIndexType
, &anItemPID
);
838 if ((pid_t
) anItemPID
== aPID
)
846 int StartupItemRun(CFMutableDictionaryRef aStatusDict
, CFMutableDictionaryRef anItem
, Action anAction
)
849 CFArrayRef aProvidesList
= CFDictionaryGetValue(anItem
, kProvidesKey
);
850 static const CFStringRef stubitems
[] = {
851 CFSTR("BootROMUpdater"), /* 3893064 */
852 CFSTR("FCUUpdater"), /* 3893064 */
853 CFSTR("AutoProtect Daemon"), /* 3965785 */
854 CFSTR("Check For Missed Tasks"), /* 3965785 */
855 CFSTR("Privacy"), /* 3933484 */
856 CFSTR("Firmware Update Checking"), /* 4001504 */
858 CFSTR("M-Audio FireWire Audio Support"), /* 3931757 */
859 CFSTR("help for M-Audio Delta Family"), /* 3931757 */
860 CFSTR("help for M-Audio Devices"), /* 3931757 */
861 CFSTR("help for M-Audio Revo 5.1"), /* 3931757 */
862 CFSTR("M-Audio USB Duo Configuration Service"), /* 3931757 */
863 CFSTR("firmware loader for M-Audio devices"), /* 3931757 */
864 CFSTR("M-Audio MobilePre USB Configuration Service"), /* 3931757 */
865 CFSTR("M-Audio OmniStudio USB Configuration Service"), /* 3931757 */
866 CFSTR("M-Audio Transit USB Configuration Service"), /* 3931757 */
867 CFSTR("M-Audio Audiophile USB Configuration Service"), /* 3931757 */
870 const CFStringRef
*c
;
872 if (aProvidesList
&& anAction
== kActionStop
) {
873 CFIndex aProvidesCount
= CFArrayGetCount(aProvidesList
);
874 for (c
= stubitems
; *c
; c
++) {
875 if (CFArrayContainsValue(aProvidesList
, CFRangeMake(0, aProvidesCount
), *c
)) {
877 CFNumberRef aProcessNumber
= CFNumberCreate(NULL
, kCFNumberCFIndexType
, &aPID
);
879 CFDictionarySetValue(anItem
, kPIDKey
, aProcessNumber
);
880 CFRelease(aProcessNumber
);
882 StartupItemExit(aStatusDict
, anItem
, TRUE
);
888 if (anAction
== kActionNone
) {
889 StartupItemExit(aStatusDict
, anItem
, TRUE
);
892 CFStringRef aBundlePathString
= CFDictionaryGetValue(anItem
, kBundlePathKey
);
893 size_t aBundlePathCLength
=
894 CFStringGetMaximumSizeForEncoding(CFStringGetLength(aBundlePathString
), kCFStringEncodingUTF8
) + 1;
895 char *aBundlePath
= (char *)malloc(aBundlePathCLength
);
896 char anExecutable
[PATH_MAX
] = "";
899 syslog(LOG_EMERG
, "malloc() failed; out of memory while running item %s", aBundlePathString
);
902 if (!CFStringGetCString(aBundlePathString
, aBundlePath
, aBundlePathCLength
, kCFStringEncodingUTF8
)) {
903 CF_syslog(LOG_EMERG
, CFSTR("Internal error while running item %@"), aBundlePathString
);
906 /* Compute path to excecutable */
909 strncpy(anExecutable
, aBundlePath
, sizeof(anExecutable
)); /* .../foo */
910 tmp
= rindex(anExecutable
, '/'); /* /foo */
911 strncat(anExecutable
, tmp
, strlen(tmp
)); /* .../foo/foo */
920 if (access(anExecutable
, X_OK
)) {
922 * Add PID key so that this item is marked as having
926 CFNumberRef aProcessNumber
= CFNumberCreate(NULL
, kCFNumberCFIndexType
, &aPID
);
928 CFDictionarySetValue(anItem
, kPIDKey
, aProcessNumber
);
929 CFRelease(aProcessNumber
);
931 CFDictionarySetValue(anItem
, kErrorKey
, kErrorPermissions
);
932 StartupItemExit(aStatusDict
, anItem
, FALSE
);
933 syslog(LOG_ERR
, "No executable file %s", anExecutable
);
935 pid_t aProccessID
= fork();
937 switch (aProccessID
) {
938 case -1: /* SystemStarter (fork failed) */
939 CFDictionarySetValue(anItem
, kErrorKey
, kErrorFork
);
940 StartupItemExit(aStatusDict
, anItem
, FALSE
);
942 CF_syslog(LOG_ERR
, CFSTR("Failed to fork for item %@: %s"), aBundlePathString
, strerror(errno
));
946 default: /* SystemStarter (fork succeeded) */
948 CFIndex aPID
= (CFIndex
) aProccessID
;
949 CFNumberRef aProcessNumber
= CFNumberCreate(NULL
, kCFNumberCFIndexType
, &aPID
);
951 CFDictionarySetValue(anItem
, kPIDKey
, aProcessNumber
);
952 CFRelease(aProcessNumber
);
954 syslog(LOG_DEBUG
, "Running command (%d): %s %s",
955 aProccessID
, anExecutable
, argumentForAction(anAction
));
962 setpriority(PRIO_PROCESS
, 0, 0);
964 syslog(LOG_WARNING
, "Unable to create session for item %s: %m", anExecutable
);
966 anError
= execl(anExecutable
, anExecutable
, argumentForAction(anAction
), NULL
);
968 /* We shouldn't get here. */
970 syslog(LOG_ERR
, "execl(\"%s\"): %m", anExecutable
);
982 StartupItemSetStatus(CFMutableDictionaryRef aStatusDict
, CFMutableDictionaryRef anItem
, CFStringRef aServiceName
,
983 Boolean aSuccess
, Boolean aReplaceFlag
)
985 void (*anAction
) (CFMutableDictionaryRef
, const void *, const void *) = aReplaceFlag
?
986 CFDictionarySetValue
: CFDictionaryAddValue
;
988 if (aStatusDict
&& anItem
) {
989 CFArrayRef aProvidesList
= CFDictionaryGetValue(anItem
, kProvidesKey
);
991 CFIndex aProvidesCount
= CFArrayGetCount(aProvidesList
);
992 CFIndex aProvidesIndex
;
995 * If a service name was specified, and it is valid,
998 if (aServiceName
&& CFArrayContainsValue(aProvidesList
, CFRangeMake(0, aProvidesCount
), aServiceName
)) {
999 aProvidesList
= CFArrayCreate(NULL
, (const void **)&aServiceName
, 1, &kCFTypeArrayCallBacks
);
1002 CFRetain(aProvidesList
);
1005 for (aProvidesIndex
= 0; aProvidesIndex
< aProvidesCount
; aProvidesIndex
++) {
1006 CFStringRef aService
= CFArrayGetValueAtIndex(aProvidesList
, aProvidesIndex
);
1009 anAction(aStatusDict
, aService
, kRunSuccess
);
1011 anAction(aStatusDict
, aService
, kRunFailure
);
1014 CFRelease(aProvidesList
);
1019 void StartupItemExit(CFMutableDictionaryRef aStatusDict
, CFMutableDictionaryRef anItem
, Boolean aSuccess
)
1021 StartupItemSetStatus(aStatusDict
, anItem
, NULL
, aSuccess
, FALSE
);