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
++) {
186 CFArrayAppendValue(aNewList
, ci
);
187 CF_syslog(LOG_DEBUG
, CFSTR("%@: Keeping %@"), type
, ci
);
191 CFDictionaryReplaceValue(aConfig
, type
, aNewList
);
194 if (type
== kUsesKey
)
200 CFIndex
StartupItemListCountServices(CFArrayRef anItemList
)
205 CFIndex anItemCount
= CFArrayGetCount(anItemList
);
206 CFIndex anItemIndex
= 0;
208 for (anItemIndex
= 0; anItemIndex
< anItemCount
; ++anItemIndex
) {
209 CFDictionaryRef anItem
= CFArrayGetValueAtIndex(anItemList
, anItemIndex
);
210 CFArrayRef aProvidesList
= CFDictionaryGetValue(anItem
, kProvidesKey
);
213 aResult
+= CFArrayGetCount(aProvidesList
);
219 bool StartupItemSecurityCheck(const char *aPath
)
221 static struct timeval boot_time
;
222 struct stat aStatBuf
;
225 if (boot_time
.tv_sec
== 0) {
226 int mib
[] = { CTL_KERN
, KERN_BOOTTIME
};
227 size_t boot_time_sz
= sizeof(boot_time
);
230 rv
= sysctl(mib
, sizeof(mib
) / sizeof(mib
[0]), &boot_time
, &boot_time_sz
, NULL
, 0);
233 assert(boot_time_sz
== sizeof(boot_time
));
236 /* should use lstatx_np() on Tiger? */
237 if (lstat(aPath
, &aStatBuf
) == -1) {
239 syslog(LOG_ERR
, "lstat(\"%s\"): %m", aPath
);
243 * We check the boot time because of 5409386.
244 * We ignore the boot time if PPID != 1 because of 5503536.
246 if ((aStatBuf
.st_ctimespec
.tv_sec
> boot_time
.tv_sec
) && (getppid() == 1)) {
247 syslog(LOG_WARNING
, "\"%s\" failed sanity check: path was created after boot up", aPath
);
250 if (!(S_ISREG(aStatBuf
.st_mode
) || S_ISDIR(aStatBuf
.st_mode
))) {
251 syslog(LOG_WARNING
, "\"%s\" failed security check: not a directory or regular file", aPath
);
254 if (aStatBuf
.st_mode
& S_IWOTH
) {
255 syslog(LOG_WARNING
, "\"%s\" failed security check: world writable", aPath
);
258 if (aStatBuf
.st_mode
& S_IWGRP
) {
259 syslog(LOG_WARNING
, "\"%s\" failed security check: group writable", aPath
);
262 if (aStatBuf
.st_uid
!= 0) {
263 syslog(LOG_WARNING
, "\"%s\" failed security check: not owned by UID 0", aPath
);
266 if (aStatBuf
.st_gid
!= 0) {
267 syslog(LOG_WARNING
, "\"%s\" failed security check: not owned by GID 0", aPath
);
271 mkdir(kFixerDir
, ACCESSPERMS
);
272 close(open(kFixerPath
, O_RDWR
|O_CREAT
|O_NOCTTY
, DEFFILEMODE
));
277 CFMutableArrayRef
StartupItemListCreateWithMask(NSSearchPathDomainMask aMask
)
279 CFMutableArrayRef anItemList
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
281 char aPath
[PATH_MAX
];
282 CFIndex aDomainIndex
= 0;
284 NSSearchPathEnumerationState aState
= NSStartSearchPathEnumeration(NSLibraryDirectory
, aMask
);
286 while ((aState
= NSGetNextSearchPathEnumeration(aState
, aPath
))) {
289 strlcat(aPath
, kStartupItemsPath
, sizeof(aPath
));
296 mkdir(aPath
, S_IRWXU
|S_IRGRP
|S_IXGRP
|S_IROTH
|S_IXOTH
);
298 if (!StartupItemSecurityCheck(aPath
))
301 if ((aDirectory
= opendir(aPath
))) {
302 struct dirent
*aBundle
;
304 while ((aBundle
= readdir(aDirectory
))) {
305 struct stat aStatBuf
;
306 char *aBundleName
= aBundle
->d_name
;
307 char aBundlePath
[PATH_MAX
];
308 char aBundleExecutablePath
[PATH_MAX
];
309 char aConfigFile
[PATH_MAX
];
310 char aDisabledFile
[PATH_MAX
];
312 if (aBundleName
[0] == '.')
315 syslog(LOG_DEBUG
, "Found item: %s", aBundleName
);
317 sprintf(aBundlePath
, "%s/%s", aPath
, aBundleName
);
318 sprintf(aBundleExecutablePath
, "%s/%s", aBundlePath
, aBundleName
);
319 sprintf(aConfigFile
, "%s/%s", aBundlePath
, kParametersFile
);
320 sprintf(aDisabledFile
, "%s/%s", aBundlePath
, kDisabledFile
);
322 if (lstat(aDisabledFile
, &aStatBuf
) == 0) {
323 syslog(LOG_NOTICE
, "Skipping disabled StartupItem: %s", aBundlePath
);
326 if (!StartupItemSecurityCheck(aBundlePath
))
328 if (!StartupItemSecurityCheck(aBundleExecutablePath
))
330 if (!StartupItemSecurityCheck(aConfigFile
))
333 /* Stow away the plist data for each bundle */
335 int aConfigFileDescriptor
;
337 if ((aConfigFileDescriptor
= open(aConfigFile
, O_RDONLY
|O_NOCTTY
, (mode_t
) 0)) != -1) {
338 struct stat aConfigFileStatBuffer
;
340 if (stat(aConfigFile
, &aConfigFileStatBuffer
) != -1) {
341 off_t aConfigFileContentsSize
= aConfigFileStatBuffer
.st_size
;
342 char *aConfigFileContentsBuffer
;
344 if ((aConfigFileContentsBuffer
=
345 mmap((caddr_t
) 0, aConfigFileContentsSize
,
346 PROT_READ
, MAP_FILE
| MAP_PRIVATE
,
347 aConfigFileDescriptor
, (off_t
) 0)) != (caddr_t
) - 1) {
348 CFDataRef aConfigData
= NULL
;
349 CFMutableDictionaryRef aConfig
= NULL
;
352 CFDataCreateWithBytesNoCopy(NULL
,
353 (const UInt8
*)aConfigFileContentsBuffer
,
354 aConfigFileContentsSize
,
358 aConfig
= (CFMutableDictionaryRef
)
359 CFPropertyListCreateFromXMLData(NULL
, aConfigData
,
360 kCFPropertyListMutableContainers
,
363 if (StartupItemValidate(aConfig
)) {
364 CFStringRef aBundlePathString
=
365 CFStringCreateWithCString(NULL
, aBundlePath
,
366 kCFStringEncodingUTF8
);
368 CFNumberRef aDomainNumber
=
369 CFNumberCreate(NULL
, kCFNumberCFIndexType
,
372 CFDictionarySetValue(aConfig
, kBundlePathKey
,
374 CFDictionarySetValue(aConfig
, kDomainKey
, aDomainNumber
);
375 CFRelease(aDomainNumber
);
376 SpecialCasesStartupItemHandler(aConfig
);
377 CFArrayAppendValue(anItemList
, aConfig
);
379 CFRelease(aBundlePathString
);
381 syslog(LOG_ERR
, "Malformatted parameters file: %s",
388 CFRelease(aConfigData
);
390 if (munmap(aConfigFileContentsBuffer
, aConfigFileContentsSize
) ==
393 "Unable to unmap parameters file %s for item %s: %m",
394 aConfigFile
, aBundleName
);
398 "Unable to map parameters file %s for item %s: %m",
399 aConfigFile
, aBundleName
);
402 syslog(LOG_ERR
, "Unable to stat parameters file %s for item %s: %m",
403 aConfigFile
, aBundleName
);
406 if (close(aConfigFileDescriptor
) == -1) {
407 syslog(LOG_ERR
, "Unable to close parameters file %s for item %s: %m",
408 aConfigFile
, aBundleName
);
411 syslog(LOG_ERR
, "Unable to open parameters file %s for item %s: %m", aConfigFile
,
416 if (closedir(aDirectory
) == -1) {
417 syslog(LOG_WARNING
, "Unable to directory bundle %s: %m", aPath
);
420 if (errno
!= ENOENT
) {
421 syslog(LOG_WARNING
, "Open on directory %s failed: %m", aPath
);
430 CFMutableDictionaryRef
StartupItemListGetProvider(CFArrayRef anItemList
, CFStringRef aService
)
432 CFMutableDictionaryRef aResult
= NULL
;
433 CFMutableArrayRef aList
= startupItemListCopyMatches(anItemList
, kProvidesKey
, aService
);
435 if (aList
&& CFArrayGetCount(aList
) > 0)
436 aResult
= (CFMutableDictionaryRef
) CFArrayGetValueAtIndex(aList
, 0);
438 if (aList
) CFRelease(aList
);
443 CFArrayRef
StartupItemListCreateFromRunning(CFArrayRef anItemList
)
445 CFMutableArrayRef aResult
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
447 CFIndex anIndex
, aCount
= CFArrayGetCount(anItemList
);
448 for (anIndex
= 0; anIndex
< aCount
; ++anIndex
) {
449 CFDictionaryRef anItem
= CFArrayGetValueAtIndex(anItemList
, anIndex
);
451 CFNumberRef aPID
= CFDictionaryGetValue(anItem
, kPIDKey
);
453 CFArrayAppendValue(aResult
, anItem
);
461 * Append items in anItemList to aDependents which depend on
463 * If anAction is kActionStart, dependent items are those which
464 * require any service provided by aParentItem.
465 * If anAction is kActionStop, dependent items are those which provide
466 * any service required by aParentItem.
468 static void appendDependents(CFMutableArrayRef aDependents
, CFArrayRef anItemList
, CFDictionaryRef aParentItem
, Action anAction
)
470 CFStringRef anInnerKey
, anOuterKey
;
471 CFArrayRef anOuterList
;
473 /* Append the parent item to the list (avoiding duplicates) */
474 if (!CFArrayContainsValue(aDependents
, CFRangeMake(0, CFArrayGetCount(aDependents
)), aParentItem
))
475 CFArrayAppendValue(aDependents
, aParentItem
);
478 * Recursively append any children of the parent item for kStartAction and kStopAction.
479 * Do nothing for other actions.
483 anInnerKey
= kProvidesKey
;
484 anOuterKey
= kRequiresKey
;
487 anInnerKey
= kRequiresKey
;
488 anOuterKey
= kProvidesKey
;
494 anOuterList
= CFDictionaryGetValue(aParentItem
, anOuterKey
);
497 CFIndex anOuterCount
= CFArrayGetCount(anOuterList
);
498 CFIndex anOuterIndex
;
500 for (anOuterIndex
= 0; anOuterIndex
< anOuterCount
; anOuterIndex
++) {
501 CFStringRef anOuterElement
= CFArrayGetValueAtIndex(anOuterList
, anOuterIndex
);
502 CFIndex anItemCount
= CFArrayGetCount(anItemList
);
505 for (anItemIndex
= 0; anItemIndex
< anItemCount
; anItemIndex
++) {
506 CFDictionaryRef anItem
= CFArrayGetValueAtIndex(anItemList
, anItemIndex
);
507 CFArrayRef anInnerList
= CFDictionaryGetValue(anItem
, anInnerKey
);
510 CFArrayContainsValue(anInnerList
, CFRangeMake(0, CFArrayGetCount(anInnerList
)),
512 && !CFArrayContainsValue(aDependents
, CFRangeMake(0, CFArrayGetCount(aDependents
)), anItem
))
513 appendDependents(aDependents
, anItemList
, anItem
, anAction
);
519 CFMutableArrayRef
StartupItemListCreateDependentsList(CFMutableArrayRef anItemList
, CFStringRef aService
, Action anAction
)
521 CFMutableArrayRef aDependents
= NULL
;
522 CFMutableDictionaryRef anItem
= NULL
;
524 if (anItemList
&& aService
)
525 anItem
= StartupItemListGetProvider(anItemList
, aService
);
532 aDependents
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
535 CF_syslog(LOG_EMERG
, CFSTR("Failed to allocate dependancy list for item %@"), anItem
);
538 appendDependents(aDependents
, anItemList
, anItem
, anAction
);
549 * countUnmetRequirements counts the number of items in anItemList
550 * which are pending in aStatusDict.
552 static int countUnmetRequirements(CFDictionaryRef aStatusDict
, CFArrayRef anItemList
)
555 CFIndex anItemCount
= CFArrayGetCount(anItemList
);
558 for (anItemIndex
= 0; anItemIndex
< anItemCount
; anItemIndex
++) {
559 CFStringRef anItem
= CFArrayGetValueAtIndex(anItemList
, anItemIndex
);
560 CFStringRef aStatus
= CFDictionaryGetValue(aStatusDict
, anItem
);
562 if (!aStatus
|| !CFEqual(aStatus
, kRunSuccess
)) {
563 CF_syslog(LOG_DEBUG
, CFSTR("\tFailed requirement/uses: %@"), anItem
);
572 * countDependantsPresent counts the number of items in aWaitingList
573 * which depend on items in anItemList.
575 static int countDependantsPresent(CFArrayRef aWaitingList
, CFArrayRef anItemList
, CFStringRef aKey
)
578 CFIndex anItemCount
= CFArrayGetCount(anItemList
);
581 for (anItemIndex
= 0; anItemIndex
< anItemCount
; anItemIndex
++) {
582 CFStringRef anItem
= CFArrayGetValueAtIndex(anItemList
, anItemIndex
);
583 CFArrayRef aMatchesList
= startupItemListCopyMatches(aWaitingList
, aKey
, anItem
);
586 aCount
= aCount
+ CFArrayGetCount(aMatchesList
);
587 CFRelease(aMatchesList
);
595 * pendingAntecedents returns TRUE if any antecedents of this item
596 * are currently running, have not yet run, or none exist.
599 pendingAntecedents(CFArrayRef aWaitingList
, CFDictionaryRef aStatusDict
, CFArrayRef anAntecedentList
, Action anAction
)
601 int aPendingFlag
= FALSE
;
603 CFIndex anAntecedentCount
= CFArrayGetCount(anAntecedentList
);
604 CFIndex anAntecedentIndex
;
606 for (anAntecedentIndex
= 0; anAntecedentIndex
< anAntecedentCount
; ++anAntecedentIndex
) {
607 CFStringRef anAntecedent
= CFArrayGetValueAtIndex(anAntecedentList
, anAntecedentIndex
);
608 CFStringRef aKey
= (anAction
== kActionStart
) ? kProvidesKey
: kUsesKey
;
609 CFArrayRef aMatchesList
= startupItemListCopyMatches(aWaitingList
, aKey
, anAntecedent
);
612 CFIndex aMatchesListCount
= CFArrayGetCount(aMatchesList
);
613 CFIndex aMatchesListIndex
;
615 for (aMatchesListIndex
= 0; aMatchesListIndex
< aMatchesListCount
; ++aMatchesListIndex
) {
616 CFDictionaryRef anItem
= CFArrayGetValueAtIndex(aMatchesList
, aMatchesListIndex
);
619 !CFDictionaryGetValue(anItem
, kPIDKey
) || !CFDictionaryGetValue(aStatusDict
, anAntecedent
)) {
625 CFRelease(aMatchesList
);
631 return (aPendingFlag
);
635 * checkForDuplicates returns TRUE if an item provides the same service as a
636 * pending item, or an item that already succeeded.
638 static Boolean
checkForDuplicates(CFArrayRef aWaitingList
, CFDictionaryRef aStatusDict
, CFDictionaryRef anItem
)
640 int aDuplicateFlag
= FALSE
;
642 CFArrayRef aProvidesList
= CFDictionaryGetValue(anItem
, kProvidesKey
);
643 CFIndex aProvidesCount
= aProvidesList
? CFArrayGetCount(aProvidesList
) : 0;
644 CFIndex aProvidesIndex
;
646 for (aProvidesIndex
= 0; aProvidesIndex
< aProvidesCount
; ++aProvidesIndex
) {
647 CFStringRef aProvides
= CFArrayGetValueAtIndex(aProvidesList
, aProvidesIndex
);
649 /* If the service succeeded, return true. */
650 CFStringRef aStatus
= CFDictionaryGetValue(aStatusDict
, aProvides
);
651 if (aStatus
&& CFEqual(aStatus
, kRunSuccess
)) {
652 aDuplicateFlag
= TRUE
;
656 * Otherwise test if any item is currently running which
657 * might provide that service.
660 CFArrayRef aMatchesList
= startupItemListCopyMatches(aWaitingList
, kProvidesKey
, aProvides
);
662 CFIndex aMatchesListCount
= CFArrayGetCount(aMatchesList
);
663 CFIndex aMatchesListIndex
;
665 for (aMatchesListIndex
= 0; aMatchesListIndex
< aMatchesListCount
; ++aMatchesListIndex
) {
666 CFDictionaryRef anDupItem
= CFArrayGetValueAtIndex(aMatchesList
, aMatchesListIndex
);
667 if (anDupItem
&& CFDictionaryGetValue(anDupItem
, kPIDKey
)) {
669 * Item is running, avoid
672 aDuplicateFlag
= TRUE
;
675 CFNumberRef anItemDomain
= CFDictionaryGetValue(anItem
, kDomainKey
);
676 CFNumberRef anotherItemDomain
= CFDictionaryGetValue(anDupItem
, kDomainKey
);
678 * If anItem was found later
679 * than aDupItem, stall
680 * anItem until aDupItem
685 CFNumberCompare(anItemDomain
, anotherItemDomain
,
686 NULL
) == kCFCompareGreaterThan
) {
692 aDuplicateFlag
= TRUE
;
698 CFRelease(aMatchesList
);
704 return (aDuplicateFlag
);
707 CFMutableDictionaryRef
StartupItemListGetNext(CFArrayRef aWaitingList
, CFDictionaryRef aStatusDict
, Action anAction
)
709 CFMutableDictionaryRef aNextItem
= NULL
;
710 CFIndex aWaitingCount
= CFArrayGetCount(aWaitingList
);
711 int aMinFailedAntecedents
= INT_MAX
;
712 CFIndex aWaitingIndex
;
725 if (!aWaitingList
|| !aStatusDict
|| aWaitingCount
<= 0)
729 * Iterate through the items in aWaitingList and look for an optimally ready item.
731 for (aWaitingIndex
= 0; aWaitingIndex
< aWaitingCount
; aWaitingIndex
++) {
732 CFMutableDictionaryRef anItem
= (CFMutableDictionaryRef
) CFArrayGetValueAtIndex(aWaitingList
, aWaitingIndex
);
733 CFArrayRef anAntecedentList
;
734 int aFailedAntecedentsCount
= 0; /* Number of unmet soft
736 Boolean aBestPick
= FALSE
; /* Is this the best pick
739 /* Filter out running items. */
740 if (CFDictionaryGetValue(anItem
, kPIDKey
))
744 * Filter out dupilicate services; if someone has
745 * provided what we provide, we don't run.
747 if (checkForDuplicates(aWaitingList
, aStatusDict
, anItem
)) {
748 CF_syslog(LOG_DEBUG
, CFSTR("Skipping %@ because of duplicate service."),
749 CFDictionaryGetValue(anItem
, kDescriptionKey
));
753 * Dependencies don't matter when restarting an item;
756 if (anAction
== kActionRestart
) {
760 anAntecedentList
= CFDictionaryGetValue(anItem
, ((anAction
== kActionStart
) ? kRequiresKey
: kProvidesKey
));
762 CF_syslog(LOG_DEBUG
, CFSTR("Checking %@"), CFDictionaryGetValue(anItem
, kDescriptionKey
));
764 if (anAntecedentList
)
765 CF_syslog(LOG_DEBUG
, CFSTR("Antecedents: %@"), anAntecedentList
);
767 syslog(LOG_DEBUG
, "No antecedents");
770 * Filter out the items which have unsatisfied antecedents.
772 if (anAntecedentList
&&
773 ((anAction
== kActionStart
) ?
774 countUnmetRequirements(aStatusDict
, anAntecedentList
) :
775 countDependantsPresent(aWaitingList
, anAntecedentList
, kRequiresKey
)))
779 * anItem has all hard dependancies met; check for soft dependancies.
780 * We'll favor the item with the fewest unmet soft dependancies here.
782 anAntecedentList
= CFDictionaryGetValue(anItem
, ((anAction
== kActionStart
) ? kUsesKey
: kProvidesKey
));
784 if (anAntecedentList
)
785 CF_syslog(LOG_DEBUG
, CFSTR("Soft dependancies: %@"), anAntecedentList
);
787 syslog(LOG_DEBUG
, "No soft dependancies");
789 if (anAntecedentList
) {
790 aFailedAntecedentsCount
=
791 ((anAction
== kActionStart
) ?
792 countUnmetRequirements(aStatusDict
, anAntecedentList
) :
793 countDependantsPresent(aWaitingList
, anAntecedentList
, kUsesKey
));
795 if (aMinFailedAntecedents
> 0)
800 * anItem has unmet dependencies that will
801 * likely be met in the future, so delay it
803 if (aFailedAntecedentsCount
> 0 && pendingAntecedents(aWaitingList
, aStatusDict
, anAntecedentList
, anAction
)) {
806 if (aFailedAntecedentsCount
> 0)
807 syslog(LOG_DEBUG
, "Total: %d", aFailedAntecedentsCount
);
809 if (aFailedAntecedentsCount
> aMinFailedAntecedents
)
810 continue; /* Another item already won out */
812 if (aFailedAntecedentsCount
< aMinFailedAntecedents
)
819 * anItem has less unmet
820 * dependancies than any
821 * other item so far, so it
824 syslog(LOG_DEBUG
, "Best pick so far, based on failed dependancies (%d->%d)",
825 aMinFailedAntecedents
, aFailedAntecedentsCount
);
828 * We have a winner! Update success
829 * parameters to match anItem.
831 aMinFailedAntecedents
= aFailedAntecedentsCount
;
834 } /* End of waiting list loop. */
839 CFStringRef
StartupItemCreateDescription(CFMutableDictionaryRef anItem
)
841 CFStringRef aString
= NULL
;
844 aString
= CFDictionaryGetValue(anItem
, kDescriptionKey
);
850 pid_t
StartupItemGetPID(CFDictionaryRef anItem
)
852 CFIndex anItemPID
= 0;
853 CFNumberRef aPIDNumber
= anItem
? CFDictionaryGetValue(anItem
, kPIDKey
) : NULL
;
854 if (aPIDNumber
&& CFNumberGetValue(aPIDNumber
, kCFNumberCFIndexType
, &anItemPID
))
855 return (pid_t
) anItemPID
;
860 CFMutableDictionaryRef
StartupItemWithPID(CFArrayRef anItemList
, pid_t aPID
)
862 CFIndex anItemCount
= CFArrayGetCount(anItemList
);
865 for (anItemIndex
= 0; anItemIndex
< anItemCount
; anItemIndex
++) {
866 CFMutableDictionaryRef anItem
= (CFMutableDictionaryRef
) CFArrayGetValueAtIndex(anItemList
, anItemIndex
);
867 CFNumberRef aPIDNumber
= CFDictionaryGetValue(anItem
, kPIDKey
);
871 CFNumberGetValue(aPIDNumber
, kCFNumberCFIndexType
, &anItemPID
);
873 if ((pid_t
) anItemPID
== aPID
)
881 int StartupItemRun(CFMutableDictionaryRef aStatusDict
, CFMutableDictionaryRef anItem
, Action anAction
)
884 CFArrayRef aProvidesList
= CFDictionaryGetValue(anItem
, kProvidesKey
);
885 static const CFStringRef stubitems
[] = {
886 CFSTR("BootROMUpdater"), /* 3893064 */
887 CFSTR("FCUUpdater"), /* 3893064 */
888 CFSTR("AutoProtect Daemon"), /* 3965785 */
889 CFSTR("Check For Missed Tasks"), /* 3965785 */
890 CFSTR("Privacy"), /* 3933484 */
891 CFSTR("Firmware Update Checking"), /* 4001504 */
893 CFSTR("M-Audio FireWire Audio Support"), /* 3931757 */
894 CFSTR("help for M-Audio Delta Family"), /* 3931757 */
895 CFSTR("help for M-Audio Devices"), /* 3931757 */
896 CFSTR("help for M-Audio Revo 5.1"), /* 3931757 */
897 CFSTR("M-Audio USB Duo Configuration Service"), /* 3931757 */
898 CFSTR("firmware loader for M-Audio devices"), /* 3931757 */
899 CFSTR("M-Audio MobilePre USB Configuration Service"), /* 3931757 */
900 CFSTR("M-Audio OmniStudio USB Configuration Service"), /* 3931757 */
901 CFSTR("M-Audio Transit USB Configuration Service"), /* 3931757 */
902 CFSTR("M-Audio Audiophile USB Configuration Service"), /* 3931757 */
905 const CFStringRef
*c
;
907 if (aProvidesList
&& anAction
== kActionStop
) {
908 CFIndex aProvidesCount
= CFArrayGetCount(aProvidesList
);
909 for (c
= stubitems
; *c
; c
++) {
910 if (CFArrayContainsValue(aProvidesList
, CFRangeMake(0, aProvidesCount
), *c
)) {
912 CFNumberRef aProcessNumber
= CFNumberCreate(NULL
, kCFNumberCFIndexType
, &aPID
);
914 CFDictionarySetValue(anItem
, kPIDKey
, aProcessNumber
);
915 CFRelease(aProcessNumber
);
917 StartupItemExit(aStatusDict
, anItem
, TRUE
);
923 if (anAction
== kActionNone
) {
924 StartupItemExit(aStatusDict
, anItem
, TRUE
);
927 CFStringRef aBundlePathString
= CFDictionaryGetValue(anItem
, kBundlePathKey
);
928 char aBundlePath
[PATH_MAX
];
929 char anExecutable
[PATH_MAX
];
932 if (!CFStringGetCString(aBundlePathString
, aBundlePath
, sizeof(aBundlePath
), kCFStringEncodingUTF8
)) {
933 CF_syslog(LOG_EMERG
, CFSTR("Internal error while running item %@"), aBundlePathString
);
936 /* Compute path to excecutable */
937 tmp
= rindex(aBundlePath
, '/');
938 snprintf(anExecutable
, sizeof(anExecutable
), "%s%s", aBundlePath
, tmp
);
944 if (access(anExecutable
, X_OK
)) {
946 * Add PID key so that this item is marked as having
950 CFNumberRef aProcessNumber
= CFNumberCreate(NULL
, kCFNumberCFIndexType
, &aPID
);
952 CFDictionarySetValue(anItem
, kPIDKey
, aProcessNumber
);
953 CFRelease(aProcessNumber
);
955 CFDictionarySetValue(anItem
, kErrorKey
, kErrorPermissions
);
956 StartupItemExit(aStatusDict
, anItem
, FALSE
);
957 syslog(LOG_ERR
, "No executable file %s", anExecutable
);
959 pid_t aProccessID
= fork();
961 switch (aProccessID
) {
962 case -1: /* SystemStarter (fork failed) */
963 CFDictionarySetValue(anItem
, kErrorKey
, kErrorFork
);
964 StartupItemExit(aStatusDict
, anItem
, FALSE
);
966 CF_syslog(LOG_ERR
, CFSTR("Failed to fork for item %@: %s"), aBundlePathString
, strerror(errno
));
970 default: /* SystemStarter (fork succeeded) */
972 CFIndex aPID
= (CFIndex
) aProccessID
;
973 CFNumberRef aProcessNumber
= CFNumberCreate(NULL
, kCFNumberCFIndexType
, &aPID
);
975 CFDictionarySetValue(anItem
, kPIDKey
, aProcessNumber
);
976 CFRelease(aProcessNumber
);
978 syslog(LOG_DEBUG
, "Running command (%d): %s %s",
979 aProccessID
, anExecutable
, argumentForAction(anAction
));
987 syslog(LOG_WARNING
, "Unable to create session for item %s: %m", anExecutable
);
989 anError
= execl(anExecutable
, anExecutable
, argumentForAction(anAction
), NULL
);
991 /* We shouldn't get here. */
993 syslog(LOG_ERR
, "execl(\"%s\"): %m", anExecutable
);
1005 StartupItemSetStatus(CFMutableDictionaryRef aStatusDict
, CFMutableDictionaryRef anItem
, CFStringRef aServiceName
,
1006 Boolean aSuccess
, Boolean aReplaceFlag
)
1008 void (*anAction
) (CFMutableDictionaryRef
, const void *, const void *) = aReplaceFlag
?
1009 CFDictionarySetValue
: CFDictionaryAddValue
;
1011 if (aStatusDict
&& anItem
) {
1012 CFArrayRef aProvidesList
= CFDictionaryGetValue(anItem
, kProvidesKey
);
1013 if (aProvidesList
) {
1014 CFIndex aProvidesCount
= CFArrayGetCount(aProvidesList
);
1015 CFIndex aProvidesIndex
;
1018 * If a service name was specified, and it is valid,
1021 if (aServiceName
&& CFArrayContainsValue(aProvidesList
, CFRangeMake(0, aProvidesCount
), aServiceName
)) {
1022 aProvidesList
= CFArrayCreate(NULL
, (const void **)&aServiceName
, 1, &kCFTypeArrayCallBacks
);
1025 CFRetain(aProvidesList
);
1028 for (aProvidesIndex
= 0; aProvidesIndex
< aProvidesCount
; aProvidesIndex
++) {
1029 CFStringRef aService
= CFArrayGetValueAtIndex(aProvidesList
, aProvidesIndex
);
1032 anAction(aStatusDict
, aService
, kRunSuccess
);
1034 anAction(aStatusDict
, aService
, kRunFailure
);
1037 CFRelease(aProvidesList
);
1042 void StartupItemExit(CFMutableDictionaryRef aStatusDict
, CFMutableDictionaryRef anItem
, Boolean aSuccess
)
1044 StartupItemSetStatus(aStatusDict
, anItem
, NULL
, aSuccess
, FALSE
);