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_LICENSE_HEADER_START@
11 * Portions Copyright (c) 1999 Apple Computer, Inc. All Rights
12 * Reserved. This file contains Original Code and/or Modifications of
13 * Original Code as defined in and that are subject to the Apple Public
14 * Source License Version 1.1 (the "License"). You may not use this file
15 * except in compliance with the License. Please obtain a copy of the
16 * License at http://www.apple.com/publicsource and read it before using
19 * The Original Code and all software distributed under the License are
20 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
21 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
22 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
23 * FITNESS FOR A PARTICULAR PURPOSE OR NON- INFRINGEMENT. Please see the
24 * License for the specific language governing rights and limitations
27 * @APPLE_LICENSE_HEADER_END@
31 #include <sys/types.h>
42 #include <CoreFoundation/CoreFoundation.h>
43 #include "StartupItems.h"
45 #define kStartupItemsPath "/StartupItems"
46 #define kParametersFile "StartupParameters.plist"
47 #define kDisabledFile ".disabled"
49 #define kRunSuccess CFSTR("success")
50 #define kRunFailure CFSTR("failure")
58 kPriorityNetwork
= 10,
63 priorityFromString(CFStringRef aPriority
)
66 if (CFEqual(aPriority
, CFSTR("Last")))
68 else if (CFEqual(aPriority
, CFSTR("Late")))
70 else if (CFEqual(aPriority
, CFSTR("None")))
72 else if (CFEqual(aPriority
, CFSTR("Early")))
73 return kPriorityEarly
;
74 else if (CFEqual(aPriority
, CFSTR("First")))
75 return kPriorityFirst
;
81 argumentForAction(Action anAction
)
84 case kActionStart
:return "start";
94 #define checkTypeOfValue(aKey,aTypeID) \
96 CFStringRef aProperty = CFDictionaryGetValue(aConfig, aKey); \
97 if (aProperty && CFGetTypeID(aProperty) != aTypeID) \
102 StartupItemValidate(CFDictionaryRef aConfig
)
104 if (aConfig
&& CFGetTypeID(aConfig
) == CFDictionaryGetTypeID()) {
105 checkTypeOfValue(kProvidesKey
, CFArrayGetTypeID());
106 checkTypeOfValue(kRequiresKey
, CFArrayGetTypeID());
114 * remove item from waiting list
117 RemoveItemFromWaitingList(StartupContext aStartupContext
, CFMutableDictionaryRef anItem
)
119 /* Remove the item from the waiting list. */
120 if (aStartupContext
&& anItem
&& aStartupContext
->aWaitingList
) {
121 CFRange aRange
= {0, CFArrayGetCount(aStartupContext
->aWaitingList
)};
122 CFIndex anIndex
= CFArrayGetFirstIndexOfValue(aStartupContext
->aWaitingList
, aRange
, anItem
);
125 CFArrayRemoveValueAtIndex(aStartupContext
->aWaitingList
, anIndex
);
131 * add item to failed list, create list if it doesn't exist
132 * return and fail quietly if it can't create list
135 AddItemToFailedList(StartupContext aStartupContext
, CFMutableDictionaryRef anItem
)
137 if (aStartupContext
&& anItem
) {
138 /* create the failed list if it doesn't exist */
139 if (!aStartupContext
->aFailedList
) {
140 aStartupContext
->aFailedList
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
142 if (aStartupContext
->aFailedList
) {
143 CFArrayAppendValue(aStartupContext
->aFailedList
, anItem
);
150 * startupItemListGetMatches returns an array of items which contain the string aService in the key aKey
152 static CFMutableArrayRef
153 startupItemListGetMatches(CFArrayRef anItemList
, CFStringRef aKey
, CFStringRef aService
)
155 CFMutableArrayRef aResult
= NULL
;
157 if (anItemList
&& aKey
&& aService
) {
158 CFIndex anItemCount
= CFArrayGetCount(anItemList
);
159 CFIndex anItemIndex
= 0;
161 aResult
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
163 for (anItemIndex
= 0; anItemIndex
< anItemCount
; ++anItemIndex
) {
164 CFMutableDictionaryRef anItem
= (CFMutableDictionaryRef
) CFArrayGetValueAtIndex(anItemList
, anItemIndex
);
165 CFArrayRef aList
= CFDictionaryGetValue(anItem
, aKey
);
168 if (CFArrayContainsValue(aList
, CFRangeMake(0, CFArrayGetCount(aList
)), aService
) &&
169 !CFArrayContainsValue(aResult
, CFRangeMake(0, CFArrayGetCount(aResult
)), anItem
)) {
170 CFArrayAppendValue(aResult
, anItem
);
179 SpecialCasesStartupItemHandler(CFMutableDictionaryRef aConfig
)
181 static const CFStringRef stubitems
[] = {
183 CFSTR("System Tuning"),
184 CFSTR("SecurityServer"),
190 CFSTR("NetworkExtensions"),
191 CFSTR("DirectoryServices"),
192 CFSTR("Network Configuration"),
193 CFSTR("mDNSResponder"),
195 CFSTR("Core Graphics"),
199 CFMutableArrayRef aList
, aNewList
;
201 CFStringRef ci
, type
= kRequiresKey
;
202 const CFStringRef
*c
;
205 aList
= (CFMutableArrayRef
) CFDictionaryGetValue(aConfig
, type
);
207 aCount
= CFArrayGetCount(aList
);
209 aNewList
= CFArrayCreateMutable(kCFAllocatorDefault
, aCount
, &kCFTypeArrayCallBacks
);
211 for (i
= 0; i
< aCount
; i
++) {
212 ci
= CFArrayGetValueAtIndex(aList
, i
);
213 CF_syslog(LOG_DEBUG
, CFSTR("%@: Evaluating %@"), type
, ci
);
214 for (c
= stubitems
; *c
; c
++) {
220 CFArrayAppendValue(aNewList
, ci
);
221 CF_syslog(LOG_DEBUG
, CFSTR("%@: Keeping %@"), type
, ci
);
225 CFDictionaryReplaceValue(aConfig
, type
, aNewList
);
227 if (type
== kUsesKey
)
234 StartupItemListCountServices(CFArrayRef anItemList
)
239 CFIndex anItemCount
= CFArrayGetCount(anItemList
);
240 CFIndex anItemIndex
= 0;
242 for (anItemIndex
= 0; anItemIndex
< anItemCount
; ++anItemIndex
) {
243 CFDictionaryRef anItem
= CFArrayGetValueAtIndex(anItemList
, anItemIndex
);
244 CFArrayRef aProvidesList
= CFDictionaryGetValue(anItem
, kProvidesKey
);
247 aResult
+= CFArrayGetCount(aProvidesList
);
254 StartupItemSecurityCheck(const char *aPath
)
256 struct stat aStatBuf
;
259 /* should use lstatx_np() on Tiger? */
260 if (lstat(aPath
, &aStatBuf
) == -1) {
262 syslog(LOG_ERR
, "lstat(\"%s\"): %m", aPath
);
265 if (!(S_ISREG(aStatBuf
.st_mode
) || S_ISDIR(aStatBuf
.st_mode
))) {
266 syslog(LOG_WARNING
, "\"%s\" failed security check: not a directory or regular file", aPath
);
269 if ((aStatBuf
.st_mode
& ALLPERMS
) & ~(S_IRWXU
| S_IRGRP
| S_IXGRP
| S_IROTH
| S_IXOTH
)) {
270 syslog(LOG_WARNING
, "\"%s\" failed security check: permissions", aPath
);
273 if (aStatBuf
.st_uid
!= 0) {
274 syslog(LOG_WARNING
, "\"%s\" failed security check: not owned by UID 0", aPath
);
277 if (aStatBuf
.st_gid
!= 0) {
278 syslog(LOG_WARNING
, "\"%s\" failed security check: not owned by GID 0", aPath
);
282 mkdir(kFixerDir
, ACCESSPERMS
);
283 close(open(kFixerPath
, O_RDWR
|O_CREAT
, DEFFILEMODE
));
289 StartupItemListCreateWithMask(NSSearchPathDomainMask aMask
)
291 CFMutableArrayRef anItemList
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
293 char aPath
[PATH_MAX
];
294 CFIndex aDomainIndex
= 0;
296 NSSearchPathEnumerationState aState
= NSStartSearchPathEnumeration(NSLibraryDirectory
, aMask
);
298 while ((aState
= NSGetNextSearchPathEnumeration(aState
, aPath
))) {
301 strcpy(aPath
+ strlen(aPath
), kStartupItemsPath
);
304 if (!StartupItemSecurityCheck(aPath
))
307 if ((aDirectory
= opendir(aPath
))) {
308 struct dirent
*aBundle
;
310 while ((aBundle
= readdir(aDirectory
))) {
311 struct stat aStatBuf
;
312 char *aBundleName
= aBundle
->d_name
;
313 char aBundlePath
[PATH_MAX
];
314 char aBundleExecutablePath
[PATH_MAX
];
315 char aConfigFile
[PATH_MAX
];
316 char aDisabledFile
[PATH_MAX
];
318 if (aBundleName
[0] == '.')
321 syslog(LOG_DEBUG
, "Found item: %s", aBundleName
);
323 sprintf(aBundlePath
, "%s/%s", aPath
, aBundleName
);
324 sprintf(aBundleExecutablePath
, "%s/%s", aBundlePath
, aBundleName
);
325 sprintf(aConfigFile
, "%s/%s", aBundlePath
, kParametersFile
);
326 sprintf(aDisabledFile
, "%s/%s", aBundlePath
, kDisabledFile
);
328 if (lstat(aDisabledFile
, &aStatBuf
) == 0) {
329 syslog(LOG_NOTICE
, "Skipping disabled StartupItem: %s", aBundlePath
);
332 if (!StartupItemSecurityCheck(aBundlePath
))
334 if (!StartupItemSecurityCheck(aBundleExecutablePath
))
336 if (!StartupItemSecurityCheck(aConfigFile
))
339 /* Stow away the plist data for each bundle */
341 int aConfigFileDescriptor
;
343 if ((aConfigFileDescriptor
= open(aConfigFile
, O_RDONLY
, (mode_t
) 0)) != -1) {
344 struct stat aConfigFileStatBuffer
;
346 if (stat(aConfigFile
, &aConfigFileStatBuffer
) != -1) {
347 off_t aConfigFileContentsSize
= aConfigFileStatBuffer
.st_size
;
348 char *aConfigFileContentsBuffer
;
350 if ((aConfigFileContentsBuffer
=
351 mmap((caddr_t
) 0, aConfigFileContentsSize
,
352 PROT_READ
, MAP_FILE
| MAP_PRIVATE
,
353 aConfigFileDescriptor
, (off_t
) 0)) != (caddr_t
) - 1) {
354 CFDataRef aConfigData
= NULL
;
355 CFMutableDictionaryRef aConfig
= NULL
;
358 CFDataCreateWithBytesNoCopy(NULL
,
359 (const UInt8
*)aConfigFileContentsBuffer
,
360 aConfigFileContentsSize
,
364 aConfig
= (CFMutableDictionaryRef
)
365 CFPropertyListCreateFromXMLData(NULL
, aConfigData
,
366 kCFPropertyListMutableContainers
, NULL
);
368 if (StartupItemValidate(aConfig
)) {
369 CFStringRef aBundlePathString
=
370 CFStringCreateWithCString(NULL
, aBundlePath
, kCFStringEncodingUTF8
);
372 CFNumberRef aDomainNumber
=
373 CFNumberCreate(NULL
, kCFNumberCFIndexType
, &aDomainIndex
);
375 CFDictionarySetValue(aConfig
, kBundlePathKey
, aBundlePathString
);
376 CFDictionarySetValue(aConfig
, kDomainKey
, aDomainNumber
);
377 CFRelease(aDomainNumber
);
378 SpecialCasesStartupItemHandler(aConfig
);
379 CFArrayAppendValue(anItemList
, aConfig
);
381 CFRelease(aBundlePathString
);
383 syslog(LOG_ERR
, "Malformatted parameters file: %s", aConfigFile
);
389 CFRelease(aConfigData
);
391 if (munmap(aConfigFileContentsBuffer
, aConfigFileContentsSize
) == -1) {
392 syslog(LOG_WARNING
, "Unable to unmap parameters file %s for item %s: %m", aConfigFile
, aBundleName
);
395 syslog(LOG_ERR
, "Unable to map parameters file %s for item %s: %m", aConfigFile
, aBundleName
);
398 syslog(LOG_ERR
, "Unable to stat parameters file %s for item %s: %m", aConfigFile
, aBundleName
);
401 if (close(aConfigFileDescriptor
) == -1) {
402 syslog(LOG_ERR
, "Unable to close parameters file %s for item %s: %m", aConfigFile
, aBundleName
);
405 syslog(LOG_ERR
, "Unable to open parameters file %s for item %s: %m", aConfigFile
, aBundleName
);
409 if (closedir(aDirectory
) == -1) {
410 syslog(LOG_WARNING
, "Unable to directory bundle %s: %m", aPath
);
413 if (errno
!= ENOENT
) {
414 syslog(LOG_WARNING
, "Open on directory %s failed: %m", aPath
);
423 CFMutableDictionaryRef
424 StartupItemListGetProvider(CFArrayRef anItemList
, CFStringRef aService
)
426 CFMutableDictionaryRef aResult
= NULL
;
427 CFMutableArrayRef aList
= startupItemListGetMatches(anItemList
, kProvidesKey
, aService
);
429 if (aList
&& CFArrayGetCount(aList
) > 0)
430 aResult
= (CFMutableDictionaryRef
) CFArrayGetValueAtIndex(aList
, 0);
436 StartupItemListGetRunning(CFArrayRef anItemList
)
438 CFMutableArrayRef aResult
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
440 CFIndex anIndex
, aCount
= CFArrayGetCount(anItemList
);
441 for (anIndex
= 0; anIndex
< aCount
; ++anIndex
) {
442 CFDictionaryRef anItem
= CFArrayGetValueAtIndex(anItemList
, anIndex
);
444 CFNumberRef aPID
= CFDictionaryGetValue(anItem
, kPIDKey
);
446 CFArrayAppendValue(aResult
, anItem
);
454 * Append items in anItemList to aDependents which depend on
456 * If anAction is kActionStart, dependent items are those which
457 * require any service provided by aParentItem.
458 * If anAction is kActionStop, dependent items are those which provide
459 * any service required by aParentItem.
462 appendDependents(CFMutableArrayRef aDependents
,
463 CFArrayRef anItemList
, CFDictionaryRef aParentItem
,
466 CFStringRef anInnerKey
, anOuterKey
;
467 CFArrayRef anOuterList
;
469 /* Append the parent item to the list (avoiding duplicates) */
470 if (!CFArrayContainsValue(aDependents
, CFRangeMake(0, CFArrayGetCount(aDependents
)), aParentItem
))
471 CFArrayAppendValue(aDependents
, aParentItem
);
474 * Recursively append any children of the parent item for kStartAction and kStopAction.
475 * Do nothing for other actions.
479 anInnerKey
= kProvidesKey
;
480 anOuterKey
= kRequiresKey
;
483 anInnerKey
= kRequiresKey
;
484 anOuterKey
= kProvidesKey
;
490 anOuterList
= CFDictionaryGetValue(aParentItem
, anOuterKey
);
493 CFIndex anOuterCount
= CFArrayGetCount(anOuterList
);
494 CFIndex anOuterIndex
;
496 for (anOuterIndex
= 0; anOuterIndex
< anOuterCount
; anOuterIndex
++) {
497 CFStringRef anOuterElement
= CFArrayGetValueAtIndex(anOuterList
, anOuterIndex
);
498 CFIndex anItemCount
= CFArrayGetCount(anItemList
);
501 for (anItemIndex
= 0; anItemIndex
< anItemCount
; anItemIndex
++) {
502 CFDictionaryRef anItem
= CFArrayGetValueAtIndex(anItemList
, anItemIndex
);
503 CFArrayRef anInnerList
= CFDictionaryGetValue(anItem
, anInnerKey
);
506 CFArrayContainsValue(anInnerList
, CFRangeMake(0, CFArrayGetCount(anInnerList
)), anOuterElement
) &&
507 !CFArrayContainsValue(aDependents
, CFRangeMake(0, CFArrayGetCount(aDependents
)), anItem
))
508 appendDependents(aDependents
, anItemList
, anItem
, anAction
);
515 StartupItemListCreateDependentsList(CFMutableArrayRef anItemList
, CFStringRef aService
, Action anAction
)
517 CFMutableArrayRef aDependents
= NULL
;
518 CFMutableDictionaryRef anItem
= NULL
;
520 if (anItemList
&& aService
)
521 anItem
= StartupItemListGetProvider(anItemList
, aService
);
528 aDependents
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
531 CF_syslog(LOG_EMERG
, CFSTR("Failed to allocate dependancy list for item %@"), anItem
);
534 appendDependents(aDependents
, anItemList
, anItem
, anAction
);
545 * countUnmetRequirements counts the number of items in anItemList
546 * which are pending in aStatusDict.
549 countUnmetRequirements(CFDictionaryRef aStatusDict
, CFArrayRef anItemList
)
552 CFIndex anItemCount
= CFArrayGetCount(anItemList
);
555 for (anItemIndex
= 0; anItemIndex
< anItemCount
; anItemIndex
++) {
556 CFStringRef anItem
= CFArrayGetValueAtIndex(anItemList
, anItemIndex
);
557 CFStringRef aStatus
= CFDictionaryGetValue(aStatusDict
, anItem
);
559 if (!aStatus
|| !CFEqual(aStatus
, kRunSuccess
)) {
560 CF_syslog(LOG_DEBUG
, CFSTR("\tFailed requirement/uses: %@"), anItem
);
569 * countDependantsPresent counts the number of items in aWaitingList
570 * which depend on items in anItemList.
573 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
) ||
618 !CFDictionaryGetValue(aStatusDict
, anAntecedent
)) {
624 CFRelease(aMatchesList
);
630 return (aPendingFlag
);
634 * checkForDuplicates returns TRUE if an item provides the same service as a
635 * pending item, or an item that already succeeded.
638 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
= startupItemListGetMatches(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
, NULL
) == kCFCompareGreaterThan
) {
691 aDuplicateFlag
= TRUE
;
697 CFRelease(aMatchesList
);
703 return (aDuplicateFlag
);
706 CFMutableDictionaryRef
707 StartupItemListGetNext(CFArrayRef aWaitingList
, CFDictionaryRef aStatusDict
, Action anAction
)
709 CFMutableDictionaryRef aNextItem
= NULL
;
710 CFIndex aWaitingCount
= CFArrayGetCount(aWaitingList
);
723 if (aWaitingList
&& aStatusDict
&& aWaitingCount
> 0) {
724 Priority aMaxPriority
= kPriorityLast
;
725 int aMinFailedAntecedents
= INT_MAX
;
726 CFIndex aWaitingIndex
;
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
;
735 /* Filter out running items. */
736 if (CFDictionaryGetValue(anItem
, kPIDKey
))
740 * Filter out dupilicate services; if someone has
741 * provided what we provide, we don't run.
743 if (checkForDuplicates(aWaitingList
, aStatusDict
, anItem
)) {
744 CF_syslog(LOG_DEBUG
, CFSTR("Skipping %@ because of duplicate service."), CFDictionaryGetValue(anItem
, kDescriptionKey
));
748 * Dependencies don't matter when restarting an item;
751 if (anAction
== kActionRestart
) {
755 anAntecedentList
= CFDictionaryGetValue(anItem
, ((anAction
== kActionStart
) ? kRequiresKey
: kProvidesKey
));
757 CF_syslog(LOG_DEBUG
, CFSTR("Checking %@"), CFDictionaryGetValue(anItem
, kDescriptionKey
));
759 if (anAntecedentList
)
760 CF_syslog(LOG_DEBUG
, CFSTR("Antecedents: %@"), anAntecedentList
);
762 syslog(LOG_DEBUG
, "No antecedents");
765 * Filter out the items which have unsatisfied antecedents.
767 if (anAntecedentList
&&
768 ((anAction
== kActionStart
) ?
769 countUnmetRequirements(aStatusDict
, anAntecedentList
) :
770 countDependantsPresent(aWaitingList
, anAntecedentList
, kRequiresKey
)))
774 * anItem has all hard dependancies met; check for soft dependancies.
775 * We'll favor the item with the fewest unmet soft dependancies here.
778 int aFailedAntecedentsCount
= 0; /* Number of unmet soft
780 Boolean aBestPick
= FALSE
; /* Is this the best pick
783 anAntecedentList
= CFDictionaryGetValue(anItem
, ((anAction
== kActionStart
) ?
784 kUsesKey
: kProvidesKey
));
786 if (anAntecedentList
)
787 CF_syslog(LOG_DEBUG
, CFSTR("Soft dependancies: %@"), anAntecedentList
);
789 syslog(LOG_DEBUG
, "No soft dependancies");
791 if (anAntecedentList
) {
792 aFailedAntecedentsCount
=
793 ((anAction
== kActionStart
) ?
794 countUnmetRequirements(aStatusDict
, anAntecedentList
) :
795 countDependantsPresent(aWaitingList
, anAntecedentList
, kUsesKey
));
797 if (aMinFailedAntecedents
> 0)
802 * anItem has unmet dependencies that will
803 * likely be met in the future, so delay it
805 if (aFailedAntecedentsCount
> 0 &&
806 pendingAntecedents(aWaitingList
, aStatusDict
, anAntecedentList
, anAction
)) {
809 if (aFailedAntecedentsCount
> 0)
810 syslog(LOG_DEBUG
, "Total: %d", aFailedAntecedentsCount
);
812 if (aFailedAntecedentsCount
> aMinFailedAntecedents
)
813 goto next_item
; /* Another item already
815 if (aFailedAntecedentsCount
< aMinFailedAntecedents
)
819 Priority aPriority
= priorityFromString(CFDictionaryGetValue(anItem
, kPriorityKey
));
823 * anItem has less unmet
824 * dependancies than any
825 * other item so far, so it
828 syslog(LOG_DEBUG
, "Best pick so far, based on failed dependancies (%d->%d)",
829 aMinFailedAntecedents
, aFailedAntecedentsCount
);
830 } else if ((anAction
== kActionStart
) ?
831 (aPriority
>= aMaxPriority
) :
832 (aPriority
<= aMaxPriority
)) {
835 * priority, so it wins.
837 syslog(LOG_DEBUG
, "Best pick so far, based on Priority (%d->%d)",
838 aMaxPriority
, aPriority
);
840 goto next_item
; /* No soup for you! */
843 * We have a winner! Update success
844 * parameters to match anItem.
846 aMinFailedAntecedents
= aFailedAntecedentsCount
;
847 aMaxPriority
= aPriority
;
851 } /* End of uses section. */
856 } /* End of waiting list loop. */
858 } /* if (aWaitingList && aWaitingCount > 0) */
863 StartupItemGetDescription(CFMutableDictionaryRef anItem
)
865 CFStringRef aString
= NULL
;
868 aString
= CFDictionaryGetValue(anItem
, kDescriptionKey
);
875 StartupItemGetPID(CFDictionaryRef anItem
)
877 CFIndex anItemPID
= 0;
878 CFNumberRef aPIDNumber
= anItem
? CFDictionaryGetValue(anItem
, kPIDKey
) : NULL
;
879 if (aPIDNumber
&& CFNumberGetValue(aPIDNumber
, kCFNumberCFIndexType
, &anItemPID
))
880 return (pid_t
) anItemPID
;
885 CFMutableDictionaryRef
886 StartupItemWithPID(CFArrayRef anItemList
, pid_t aPID
)
888 CFIndex anItemCount
= CFArrayGetCount(anItemList
);
891 for (anItemIndex
= 0; anItemIndex
< anItemCount
; anItemIndex
++) {
892 CFMutableDictionaryRef anItem
= (CFMutableDictionaryRef
) CFArrayGetValueAtIndex(anItemList
, anItemIndex
);
893 CFNumberRef aPIDNumber
= CFDictionaryGetValue(anItem
, kPIDKey
);
897 CFNumberGetValue(aPIDNumber
, kCFNumberCFIndexType
, &anItemPID
);
899 if ((pid_t
) anItemPID
== aPID
)
908 StartupItemRun(CFMutableDictionaryRef aStatusDict
, CFMutableDictionaryRef anItem
, Action anAction
)
911 CFArrayRef aProvidesList
= CFDictionaryGetValue(anItem
, kProvidesKey
);
912 static const CFStringRef stubitems
[] = {
913 CFSTR("BootROMUpdater"), /* 3893064 */
914 CFSTR("FCUUpdater"), /* 3893064 */
915 CFSTR("AutoProtect Daemon"), /* 3965785 */
916 CFSTR("Check For Missed Tasks"), /* 3965785 */
917 CFSTR("Privacy"), /* 3933484 */
918 CFSTR("Firmware Update Checking"), /* 4001504 */
920 CFSTR("M-Audio FireWire Audio Support"), /* 3931757 */
921 CFSTR("help for M-Audio Delta Family"), /* 3931757 */
922 CFSTR("help for M-Audio Devices"), /* 3931757 */
923 CFSTR("help for M-Audio Revo 5.1"), /* 3931757 */
924 CFSTR("M-Audio USB Duo Configuration Service"), /* 3931757 */
925 CFSTR("firmware loader for M-Audio devices"), /* 3931757 */
926 CFSTR("M-Audio MobilePre USB Configuration Service"), /* 3931757 */
927 CFSTR("M-Audio OmniStudio USB Configuration Service"), /* 3931757 */
928 CFSTR("M-Audio Transit USB Configuration Service"), /* 3931757 */
929 CFSTR("M-Audio Audiophile USB Configuration Service"), /* 3931757 */
932 const CFStringRef
*c
;
934 if (aProvidesList
&& anAction
== kActionStop
) {
935 CFIndex aProvidesCount
= CFArrayGetCount(aProvidesList
);
936 for (c
= stubitems
; *c
; c
++) {
937 if (CFArrayContainsValue(aProvidesList
, CFRangeMake(0, aProvidesCount
), *c
)) {
939 CFNumberRef aProcessNumber
= CFNumberCreate(NULL
, kCFNumberCFIndexType
, &aPID
);
941 CFDictionarySetValue(anItem
, kPIDKey
, aProcessNumber
);
942 CFRelease(aProcessNumber
);
944 StartupItemExit(aStatusDict
, anItem
, TRUE
);
950 if (anAction
== kActionNone
) {
951 StartupItemExit(aStatusDict
, anItem
, TRUE
);
954 CFStringRef aBundlePathString
= CFDictionaryGetValue(anItem
, kBundlePathKey
);
955 size_t aBundlePathCLength
=
956 CFStringGetMaximumSizeForEncoding(CFStringGetLength(aBundlePathString
), kCFStringEncodingUTF8
) + 1;
957 char *aBundlePath
= (char *) malloc(aBundlePathCLength
);
958 char anExecutable
[PATH_MAX
] = "";
961 syslog(LOG_EMERG
, "malloc() failed; out of memory while running item %s", aBundlePathString
);
964 if (!CFStringGetCString(aBundlePathString
, aBundlePath
, aBundlePathCLength
, kCFStringEncodingUTF8
)) {
965 CF_syslog(LOG_EMERG
, CFSTR("Internal error while running item %@"), aBundlePathString
);
968 /* Compute path to excecutable */
971 strncpy(anExecutable
, aBundlePath
, sizeof(anExecutable
)); /* .../foo */
972 tmp
= rindex(anExecutable
, '/'); /* /foo */
973 strncat(anExecutable
, tmp
, strlen(tmp
)); /* .../foo/foo */
982 if (access(anExecutable
, X_OK
)) {
984 * Add PID key so that this item is marked as having
988 CFNumberRef aProcessNumber
= CFNumberCreate(NULL
, kCFNumberCFIndexType
, &aPID
);
990 CFDictionarySetValue(anItem
, kPIDKey
, aProcessNumber
);
991 CFRelease(aProcessNumber
);
993 CFDictionarySetValue(anItem
, kErrorKey
, kErrorPermissions
);
994 StartupItemExit(aStatusDict
, anItem
, FALSE
);
995 syslog(LOG_ERR
, "No executable file %s", anExecutable
);
997 pid_t aProccessID
= fork();
999 switch (aProccessID
) {
1000 case -1: /* SystemStarter (fork failed) */
1001 CFDictionarySetValue(anItem
, kErrorKey
, kErrorFork
);
1002 StartupItemExit(aStatusDict
, anItem
, FALSE
);
1004 CF_syslog(LOG_ERR
, CFSTR("Failed to fork for item %@: %s"),
1005 aBundlePathString
, strerror(errno
));
1009 default: /* SystemStarter (fork succeeded) */
1011 CFIndex aPID
= (CFIndex
) aProccessID
;
1012 CFNumberRef aProcessNumber
= CFNumberCreate(NULL
, kCFNumberCFIndexType
, &aPID
);
1014 CFDictionarySetValue(anItem
, kPIDKey
, aProcessNumber
);
1015 CFRelease(aProcessNumber
);
1017 syslog(LOG_DEBUG
, "Running command (%d): %s %s",
1018 aProccessID
, anExecutable
, argumentForAction(anAction
));
1025 setpriority(PRIO_PROCESS
, 0, 0);
1027 syslog(LOG_WARNING
, "Unable to create session for item %s: %m", anExecutable
);
1029 anError
= execl(anExecutable
, anExecutable
, argumentForAction(anAction
), NULL
);
1031 /* We shouldn't get here. */
1033 syslog(LOG_ERR
, "execl(\"%s\"): %m", anExecutable
);
1045 StartupItemSetStatus(CFMutableDictionaryRef aStatusDict
, CFMutableDictionaryRef anItem
, CFStringRef aServiceName
, Boolean aSuccess
, Boolean aReplaceFlag
)
1047 void (*anAction
) (CFMutableDictionaryRef
, const void *, const void *) = aReplaceFlag
?
1048 CFDictionarySetValue
: CFDictionaryAddValue
;
1050 if (aStatusDict
&& anItem
) {
1051 CFArrayRef aProvidesList
= CFDictionaryGetValue(anItem
, kProvidesKey
);
1052 if (aProvidesList
) {
1053 CFIndex aProvidesCount
= CFArrayGetCount(aProvidesList
);
1054 CFIndex aProvidesIndex
;
1057 * If a service name was specified, and it is valid,
1060 if (aServiceName
&& CFArrayContainsValue(aProvidesList
, CFRangeMake(0, aProvidesCount
), aServiceName
)) {
1061 aProvidesList
= CFArrayCreate(NULL
, (const void **) &aServiceName
, 1, &kCFTypeArrayCallBacks
);
1064 CFRetain(aProvidesList
);
1067 for (aProvidesIndex
= 0; aProvidesIndex
< aProvidesCount
; aProvidesIndex
++) {
1068 CFStringRef aService
= CFArrayGetValueAtIndex(aProvidesList
, aProvidesIndex
);
1071 anAction(aStatusDict
, aService
, kRunSuccess
);
1073 anAction(aStatusDict
, aService
, kRunFailure
);
1076 CFRelease(aProvidesList
);
1082 StartupItemExit(CFMutableDictionaryRef aStatusDict
, CFMutableDictionaryRef anItem
, Boolean aSuccess
)
1084 StartupItemSetStatus(aStatusDict
, anItem
, NULL
, aSuccess
, FALSE
);