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 * startupItemListGetMatches returns an array of items which contain the string aService in the key aKey
117 static CFMutableArrayRef
startupItemListGetMatches(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
);
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 static 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 strcpy(aPath
+ strlen(aPath
), kStartupItemsPath
);
296 mkdir(aPath
, S_IRWXU
|S_IRWXG
|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
= startupItemListGetMatches(anItemList
, kProvidesKey
, aService
);
435 if (aList
&& CFArrayGetCount(aList
) > 0)
436 aResult
= (CFMutableDictionaryRef
) CFArrayGetValueAtIndex(aList
, 0);
441 CFArrayRef
StartupItemListGetRunning(CFArrayRef anItemList
)
443 CFMutableArrayRef aResult
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
445 CFIndex anIndex
, aCount
= CFArrayGetCount(anItemList
);
446 for (anIndex
= 0; anIndex
< aCount
; ++anIndex
) {
447 CFDictionaryRef anItem
= CFArrayGetValueAtIndex(anItemList
, anIndex
);
449 CFNumberRef aPID
= CFDictionaryGetValue(anItem
, kPIDKey
);
451 CFArrayAppendValue(aResult
, anItem
);
459 * Append items in anItemList to aDependents which depend on
461 * If anAction is kActionStart, dependent items are those which
462 * require any service provided by aParentItem.
463 * If anAction is kActionStop, dependent items are those which provide
464 * any service required by aParentItem.
466 static void appendDependents(CFMutableArrayRef aDependents
, CFArrayRef anItemList
, CFDictionaryRef aParentItem
, Action anAction
)
468 CFStringRef anInnerKey
, anOuterKey
;
469 CFArrayRef anOuterList
;
471 /* Append the parent item to the list (avoiding duplicates) */
472 if (!CFArrayContainsValue(aDependents
, CFRangeMake(0, CFArrayGetCount(aDependents
)), aParentItem
))
473 CFArrayAppendValue(aDependents
, aParentItem
);
476 * Recursively append any children of the parent item for kStartAction and kStopAction.
477 * Do nothing for other actions.
481 anInnerKey
= kProvidesKey
;
482 anOuterKey
= kRequiresKey
;
485 anInnerKey
= kRequiresKey
;
486 anOuterKey
= kProvidesKey
;
492 anOuterList
= CFDictionaryGetValue(aParentItem
, anOuterKey
);
495 CFIndex anOuterCount
= CFArrayGetCount(anOuterList
);
496 CFIndex anOuterIndex
;
498 for (anOuterIndex
= 0; anOuterIndex
< anOuterCount
; anOuterIndex
++) {
499 CFStringRef anOuterElement
= CFArrayGetValueAtIndex(anOuterList
, anOuterIndex
);
500 CFIndex anItemCount
= CFArrayGetCount(anItemList
);
503 for (anItemIndex
= 0; anItemIndex
< anItemCount
; anItemIndex
++) {
504 CFDictionaryRef anItem
= CFArrayGetValueAtIndex(anItemList
, anItemIndex
);
505 CFArrayRef anInnerList
= CFDictionaryGetValue(anItem
, anInnerKey
);
508 CFArrayContainsValue(anInnerList
, CFRangeMake(0, CFArrayGetCount(anInnerList
)),
510 && !CFArrayContainsValue(aDependents
, CFRangeMake(0, CFArrayGetCount(aDependents
)), anItem
))
511 appendDependents(aDependents
, anItemList
, anItem
, anAction
);
517 CFMutableArrayRef
StartupItemListCreateDependentsList(CFMutableArrayRef anItemList
, CFStringRef aService
, Action anAction
)
519 CFMutableArrayRef aDependents
= NULL
;
520 CFMutableDictionaryRef anItem
= NULL
;
522 if (anItemList
&& aService
)
523 anItem
= StartupItemListGetProvider(anItemList
, aService
);
530 aDependents
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
533 CF_syslog(LOG_EMERG
, CFSTR("Failed to allocate dependancy list for item %@"), anItem
);
536 appendDependents(aDependents
, anItemList
, anItem
, anAction
);
547 * countUnmetRequirements counts the number of items in anItemList
548 * which are pending in aStatusDict.
550 static int countUnmetRequirements(CFDictionaryRef aStatusDict
, CFArrayRef anItemList
)
553 CFIndex anItemCount
= CFArrayGetCount(anItemList
);
556 for (anItemIndex
= 0; anItemIndex
< anItemCount
; anItemIndex
++) {
557 CFStringRef anItem
= CFArrayGetValueAtIndex(anItemList
, anItemIndex
);
558 CFStringRef aStatus
= CFDictionaryGetValue(aStatusDict
, anItem
);
560 if (!aStatus
|| !CFEqual(aStatus
, kRunSuccess
)) {
561 CF_syslog(LOG_DEBUG
, CFSTR("\tFailed requirement/uses: %@"), anItem
);
570 * countDependantsPresent counts the number of items in aWaitingList
571 * which depend on items in anItemList.
573 static int countDependantsPresent(CFArrayRef aWaitingList
, CFArrayRef anItemList
, CFStringRef aKey
)
576 CFIndex anItemCount
= CFArrayGetCount(anItemList
);
579 for (anItemIndex
= 0; anItemIndex
< anItemCount
; anItemIndex
++) {
580 CFStringRef anItem
= CFArrayGetValueAtIndex(anItemList
, anItemIndex
);
581 CFArrayRef aMatchesList
= startupItemListGetMatches(aWaitingList
, aKey
, anItem
);
584 aCount
= aCount
+ CFArrayGetCount(aMatchesList
);
585 CFRelease(aMatchesList
);
593 * pendingAntecedents returns TRUE if any antecedents of this item
594 * are currently running, have not yet run, or none exist.
597 pendingAntecedents(CFArrayRef aWaitingList
, CFDictionaryRef aStatusDict
, CFArrayRef anAntecedentList
, Action anAction
)
599 int aPendingFlag
= FALSE
;
601 CFIndex anAntecedentCount
= CFArrayGetCount(anAntecedentList
);
602 CFIndex anAntecedentIndex
;
604 for (anAntecedentIndex
= 0; anAntecedentIndex
< anAntecedentCount
; ++anAntecedentIndex
) {
605 CFStringRef anAntecedent
= CFArrayGetValueAtIndex(anAntecedentList
, anAntecedentIndex
);
606 CFStringRef aKey
= (anAction
== kActionStart
) ? kProvidesKey
: kUsesKey
;
607 CFArrayRef aMatchesList
= startupItemListGetMatches(aWaitingList
, aKey
, anAntecedent
);
610 CFIndex aMatchesListCount
= CFArrayGetCount(aMatchesList
);
611 CFIndex aMatchesListIndex
;
613 for (aMatchesListIndex
= 0; aMatchesListIndex
< aMatchesListCount
; ++aMatchesListIndex
) {
614 CFDictionaryRef anItem
= CFArrayGetValueAtIndex(aMatchesList
, aMatchesListIndex
);
617 !CFDictionaryGetValue(anItem
, kPIDKey
) || !CFDictionaryGetValue(aStatusDict
, anAntecedent
)) {
623 CFRelease(aMatchesList
);
629 return (aPendingFlag
);
633 * checkForDuplicates returns TRUE if an item provides the same service as a
634 * pending item, or an item that already succeeded.
636 static Boolean
checkForDuplicates(CFArrayRef aWaitingList
, CFDictionaryRef aStatusDict
, CFDictionaryRef anItem
)
638 int aDuplicateFlag
= FALSE
;
640 CFArrayRef aProvidesList
= CFDictionaryGetValue(anItem
, kProvidesKey
);
641 CFIndex aProvidesCount
= aProvidesList
? CFArrayGetCount(aProvidesList
) : 0;
642 CFIndex aProvidesIndex
;
644 for (aProvidesIndex
= 0; aProvidesIndex
< aProvidesCount
; ++aProvidesIndex
) {
645 CFStringRef aProvides
= CFArrayGetValueAtIndex(aProvidesList
, aProvidesIndex
);
647 /* If the service succeeded, return true. */
648 CFStringRef aStatus
= CFDictionaryGetValue(aStatusDict
, aProvides
);
649 if (aStatus
&& CFEqual(aStatus
, kRunSuccess
)) {
650 aDuplicateFlag
= TRUE
;
654 * Otherwise test if any item is currently running which
655 * might provide that service.
658 CFArrayRef aMatchesList
= startupItemListGetMatches(aWaitingList
, kProvidesKey
, aProvides
);
660 CFIndex aMatchesListCount
= CFArrayGetCount(aMatchesList
);
661 CFIndex aMatchesListIndex
;
663 for (aMatchesListIndex
= 0; aMatchesListIndex
< aMatchesListCount
; ++aMatchesListIndex
) {
664 CFDictionaryRef anDupItem
= CFArrayGetValueAtIndex(aMatchesList
, aMatchesListIndex
);
665 if (anDupItem
&& CFDictionaryGetValue(anDupItem
, kPIDKey
)) {
667 * Item is running, avoid
670 aDuplicateFlag
= TRUE
;
673 CFNumberRef anItemDomain
= CFDictionaryGetValue(anItem
, kDomainKey
);
674 CFNumberRef anotherItemDomain
= CFDictionaryGetValue(anDupItem
, kDomainKey
);
676 * If anItem was found later
677 * than aDupItem, stall
678 * anItem until aDupItem
683 CFNumberCompare(anItemDomain
, anotherItemDomain
,
684 NULL
) == kCFCompareGreaterThan
) {
690 aDuplicateFlag
= TRUE
;
696 CFRelease(aMatchesList
);
702 return (aDuplicateFlag
);
705 CFMutableDictionaryRef
StartupItemListGetNext(CFArrayRef aWaitingList
, CFDictionaryRef aStatusDict
, Action anAction
)
707 CFMutableDictionaryRef aNextItem
= NULL
;
708 CFIndex aWaitingCount
= CFArrayGetCount(aWaitingList
);
709 int aMinFailedAntecedents
= INT_MAX
;
710 CFIndex aWaitingIndex
;
723 if (!aWaitingList
|| !aStatusDict
|| aWaitingCount
<= 0)
727 * Iterate through the items in aWaitingList and look for an optimally ready item.
729 for (aWaitingIndex
= 0; aWaitingIndex
< aWaitingCount
; aWaitingIndex
++) {
730 CFMutableDictionaryRef anItem
= (CFMutableDictionaryRef
) CFArrayGetValueAtIndex(aWaitingList
, aWaitingIndex
);
731 CFArrayRef anAntecedentList
;
732 int aFailedAntecedentsCount
= 0; /* Number of unmet soft
734 Boolean aBestPick
= FALSE
; /* Is this the best pick
737 /* Filter out running items. */
738 if (CFDictionaryGetValue(anItem
, kPIDKey
))
742 * Filter out dupilicate services; if someone has
743 * provided what we provide, we don't run.
745 if (checkForDuplicates(aWaitingList
, aStatusDict
, anItem
)) {
746 CF_syslog(LOG_DEBUG
, CFSTR("Skipping %@ because of duplicate service."),
747 CFDictionaryGetValue(anItem
, kDescriptionKey
));
751 * Dependencies don't matter when restarting an item;
754 if (anAction
== kActionRestart
) {
758 anAntecedentList
= CFDictionaryGetValue(anItem
, ((anAction
== kActionStart
) ? kRequiresKey
: kProvidesKey
));
760 CF_syslog(LOG_DEBUG
, CFSTR("Checking %@"), CFDictionaryGetValue(anItem
, kDescriptionKey
));
762 if (anAntecedentList
)
763 CF_syslog(LOG_DEBUG
, CFSTR("Antecedents: %@"), anAntecedentList
);
765 syslog(LOG_DEBUG
, "No antecedents");
768 * Filter out the items which have unsatisfied antecedents.
770 if (anAntecedentList
&&
771 ((anAction
== kActionStart
) ?
772 countUnmetRequirements(aStatusDict
, anAntecedentList
) :
773 countDependantsPresent(aWaitingList
, anAntecedentList
, kRequiresKey
)))
777 * anItem has all hard dependancies met; check for soft dependancies.
778 * We'll favor the item with the fewest unmet soft dependancies here.
780 anAntecedentList
= CFDictionaryGetValue(anItem
, ((anAction
== kActionStart
) ? kUsesKey
: kProvidesKey
));
782 if (anAntecedentList
)
783 CF_syslog(LOG_DEBUG
, CFSTR("Soft dependancies: %@"), anAntecedentList
);
785 syslog(LOG_DEBUG
, "No soft dependancies");
787 if (anAntecedentList
) {
788 aFailedAntecedentsCount
=
789 ((anAction
== kActionStart
) ?
790 countUnmetRequirements(aStatusDict
, anAntecedentList
) :
791 countDependantsPresent(aWaitingList
, anAntecedentList
, kUsesKey
));
793 if (aMinFailedAntecedents
> 0)
798 * anItem has unmet dependencies that will
799 * likely be met in the future, so delay it
801 if (aFailedAntecedentsCount
> 0 && pendingAntecedents(aWaitingList
, aStatusDict
, anAntecedentList
, anAction
)) {
804 if (aFailedAntecedentsCount
> 0)
805 syslog(LOG_DEBUG
, "Total: %d", aFailedAntecedentsCount
);
807 if (aFailedAntecedentsCount
> aMinFailedAntecedents
)
808 continue; /* Another item already won out */
810 if (aFailedAntecedentsCount
< aMinFailedAntecedents
)
817 * anItem has less unmet
818 * dependancies than any
819 * other item so far, so it
822 syslog(LOG_DEBUG
, "Best pick so far, based on failed dependancies (%d->%d)",
823 aMinFailedAntecedents
, aFailedAntecedentsCount
);
826 * We have a winner! Update success
827 * parameters to match anItem.
829 aMinFailedAntecedents
= aFailedAntecedentsCount
;
832 } /* End of waiting list loop. */
837 CFStringRef
StartupItemGetDescription(CFMutableDictionaryRef anItem
)
839 CFStringRef aString
= NULL
;
842 aString
= CFDictionaryGetValue(anItem
, kDescriptionKey
);
848 pid_t
StartupItemGetPID(CFDictionaryRef anItem
)
850 CFIndex anItemPID
= 0;
851 CFNumberRef aPIDNumber
= anItem
? CFDictionaryGetValue(anItem
, kPIDKey
) : NULL
;
852 if (aPIDNumber
&& CFNumberGetValue(aPIDNumber
, kCFNumberCFIndexType
, &anItemPID
))
853 return (pid_t
) anItemPID
;
858 CFMutableDictionaryRef
StartupItemWithPID(CFArrayRef anItemList
, pid_t aPID
)
860 CFIndex anItemCount
= CFArrayGetCount(anItemList
);
863 for (anItemIndex
= 0; anItemIndex
< anItemCount
; anItemIndex
++) {
864 CFMutableDictionaryRef anItem
= (CFMutableDictionaryRef
) CFArrayGetValueAtIndex(anItemList
, anItemIndex
);
865 CFNumberRef aPIDNumber
= CFDictionaryGetValue(anItem
, kPIDKey
);
869 CFNumberGetValue(aPIDNumber
, kCFNumberCFIndexType
, &anItemPID
);
871 if ((pid_t
) anItemPID
== aPID
)
879 int StartupItemRun(CFMutableDictionaryRef aStatusDict
, CFMutableDictionaryRef anItem
, Action anAction
)
882 CFArrayRef aProvidesList
= CFDictionaryGetValue(anItem
, kProvidesKey
);
883 static const CFStringRef stubitems
[] = {
884 CFSTR("BootROMUpdater"), /* 3893064 */
885 CFSTR("FCUUpdater"), /* 3893064 */
886 CFSTR("AutoProtect Daemon"), /* 3965785 */
887 CFSTR("Check For Missed Tasks"), /* 3965785 */
888 CFSTR("Privacy"), /* 3933484 */
889 CFSTR("Firmware Update Checking"), /* 4001504 */
891 CFSTR("M-Audio FireWire Audio Support"), /* 3931757 */
892 CFSTR("help for M-Audio Delta Family"), /* 3931757 */
893 CFSTR("help for M-Audio Devices"), /* 3931757 */
894 CFSTR("help for M-Audio Revo 5.1"), /* 3931757 */
895 CFSTR("M-Audio USB Duo Configuration Service"), /* 3931757 */
896 CFSTR("firmware loader for M-Audio devices"), /* 3931757 */
897 CFSTR("M-Audio MobilePre USB Configuration Service"), /* 3931757 */
898 CFSTR("M-Audio OmniStudio USB Configuration Service"), /* 3931757 */
899 CFSTR("M-Audio Transit USB Configuration Service"), /* 3931757 */
900 CFSTR("M-Audio Audiophile USB Configuration Service"), /* 3931757 */
903 const CFStringRef
*c
;
905 if (aProvidesList
&& anAction
== kActionStop
) {
906 CFIndex aProvidesCount
= CFArrayGetCount(aProvidesList
);
907 for (c
= stubitems
; *c
; c
++) {
908 if (CFArrayContainsValue(aProvidesList
, CFRangeMake(0, aProvidesCount
), *c
)) {
910 CFNumberRef aProcessNumber
= CFNumberCreate(NULL
, kCFNumberCFIndexType
, &aPID
);
912 CFDictionarySetValue(anItem
, kPIDKey
, aProcessNumber
);
913 CFRelease(aProcessNumber
);
915 StartupItemExit(aStatusDict
, anItem
, TRUE
);
921 if (anAction
== kActionNone
) {
922 StartupItemExit(aStatusDict
, anItem
, TRUE
);
925 CFStringRef aBundlePathString
= CFDictionaryGetValue(anItem
, kBundlePathKey
);
926 size_t aBundlePathCLength
=
927 CFStringGetMaximumSizeForEncoding(CFStringGetLength(aBundlePathString
), kCFStringEncodingUTF8
) + 1;
928 char *aBundlePath
= (char *)malloc(aBundlePathCLength
);
929 char anExecutable
[PATH_MAX
] = "";
932 syslog(LOG_EMERG
, "malloc() failed; out of memory while running item %s", aBundlePathString
);
935 if (!CFStringGetCString(aBundlePathString
, aBundlePath
, aBundlePathCLength
, kCFStringEncodingUTF8
)) {
936 CF_syslog(LOG_EMERG
, CFSTR("Internal error while running item %@"), aBundlePathString
);
939 /* Compute path to excecutable */
942 strncpy(anExecutable
, aBundlePath
, sizeof(anExecutable
)); /* .../foo */
943 tmp
= rindex(anExecutable
, '/'); /* /foo */
944 strncat(anExecutable
, tmp
, strlen(tmp
)); /* .../foo/foo */
953 if (access(anExecutable
, X_OK
)) {
955 * Add PID key so that this item is marked as having
959 CFNumberRef aProcessNumber
= CFNumberCreate(NULL
, kCFNumberCFIndexType
, &aPID
);
961 CFDictionarySetValue(anItem
, kPIDKey
, aProcessNumber
);
962 CFRelease(aProcessNumber
);
964 CFDictionarySetValue(anItem
, kErrorKey
, kErrorPermissions
);
965 StartupItemExit(aStatusDict
, anItem
, FALSE
);
966 syslog(LOG_ERR
, "No executable file %s", anExecutable
);
968 pid_t aProccessID
= fork();
970 switch (aProccessID
) {
971 case -1: /* SystemStarter (fork failed) */
972 CFDictionarySetValue(anItem
, kErrorKey
, kErrorFork
);
973 StartupItemExit(aStatusDict
, anItem
, FALSE
);
975 CF_syslog(LOG_ERR
, CFSTR("Failed to fork for item %@: %s"), aBundlePathString
, strerror(errno
));
979 default: /* SystemStarter (fork succeeded) */
981 CFIndex aPID
= (CFIndex
) aProccessID
;
982 CFNumberRef aProcessNumber
= CFNumberCreate(NULL
, kCFNumberCFIndexType
, &aPID
);
984 CFDictionarySetValue(anItem
, kPIDKey
, aProcessNumber
);
985 CFRelease(aProcessNumber
);
987 syslog(LOG_DEBUG
, "Running command (%d): %s %s",
988 aProccessID
, anExecutable
, argumentForAction(anAction
));
996 syslog(LOG_WARNING
, "Unable to create session for item %s: %m", anExecutable
);
998 anError
= execl(anExecutable
, anExecutable
, argumentForAction(anAction
), NULL
);
1000 /* We shouldn't get here. */
1002 syslog(LOG_ERR
, "execl(\"%s\"): %m", anExecutable
);
1014 StartupItemSetStatus(CFMutableDictionaryRef aStatusDict
, CFMutableDictionaryRef anItem
, CFStringRef aServiceName
,
1015 Boolean aSuccess
, Boolean aReplaceFlag
)
1017 void (*anAction
) (CFMutableDictionaryRef
, const void *, const void *) = aReplaceFlag
?
1018 CFDictionarySetValue
: CFDictionaryAddValue
;
1020 if (aStatusDict
&& anItem
) {
1021 CFArrayRef aProvidesList
= CFDictionaryGetValue(anItem
, kProvidesKey
);
1022 if (aProvidesList
) {
1023 CFIndex aProvidesCount
= CFArrayGetCount(aProvidesList
);
1024 CFIndex aProvidesIndex
;
1027 * If a service name was specified, and it is valid,
1030 if (aServiceName
&& CFArrayContainsValue(aProvidesList
, CFRangeMake(0, aProvidesCount
), aServiceName
)) {
1031 aProvidesList
= CFArrayCreate(NULL
, (const void **)&aServiceName
, 1, &kCFTypeArrayCallBacks
);
1034 CFRetain(aProvidesList
);
1037 for (aProvidesIndex
= 0; aProvidesIndex
< aProvidesCount
; aProvidesIndex
++) {
1038 CFStringRef aService
= CFArrayGetValueAtIndex(aProvidesList
, aProvidesIndex
);
1041 anAction(aStatusDict
, aService
, kRunSuccess
);
1043 anAction(aStatusDict
, aService
, kRunFailure
);
1046 CFRelease(aProvidesList
);
1051 void StartupItemExit(CFMutableDictionaryRef aStatusDict
, CFMutableDictionaryRef anItem
, Boolean aSuccess
)
1053 StartupItemSetStatus(aStatusDict
, anItem
, NULL
, aSuccess
, FALSE
);