Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/openlaunchd
Path: blob/master/SystemStarter/StartupItems.c
374 views
1
/**
2
* StartupItems.c - Startup Item management routines
3
* Wilfredo Sanchez | [email protected]
4
* Kevin Van Vechten | [email protected]
5
* $Apple$
6
**
7
* Copyright (c) 1999-2002 Apple Computer, Inc. All rights reserved.
8
*
9
* @APPLE_APACHE_LICENSE_HEADER_START@
10
*
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
14
*
15
* http://www.apache.org/licenses/LICENSE-2.0
16
*
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.
22
*
23
* @APPLE_APACHE_LICENSE_HEADER_END@
24
**/
25
26
#include <unistd.h>
27
#include <sys/types.h>
28
#include <sys/stat.h>
29
#include <sys/sysctl.h>
30
#include <sys/mman.h>
31
#include <stdlib.h>
32
#include <fcntl.h>
33
#include <dirent.h>
34
#include <limits.h>
35
#include <errno.h>
36
#include <string.h>
37
#include <sysexits.h>
38
#include <syslog.h>
39
#include <CoreFoundation/CoreFoundation.h>
40
#include "StartupItems.h"
41
42
#define kStartupItemsPath "/StartupItems"
43
#define kParametersFile "StartupParameters.plist"
44
#define kDisabledFile ".disabled"
45
46
#define kRunSuccess CFSTR("success")
47
#define kRunFailure CFSTR("failure")
48
49
static const char *argumentForAction(Action anAction)
50
{
51
switch (anAction) {
52
case kActionStart:
53
return "start";
54
case kActionStop:
55
return "stop";
56
case kActionRestart:
57
return "restart";
58
default:
59
return NULL;
60
}
61
}
62
63
#define checkTypeOfValue(aKey,aTypeID) \
64
{ \
65
CFStringRef aProperty = CFDictionaryGetValue(aConfig, aKey); \
66
if (aProperty && CFGetTypeID(aProperty) != aTypeID) \
67
return FALSE; \
68
}
69
70
static int StartupItemValidate(CFDictionaryRef aConfig)
71
{
72
if (aConfig && CFGetTypeID(aConfig) == CFDictionaryGetTypeID()) {
73
checkTypeOfValue(kProvidesKey, CFArrayGetTypeID());
74
checkTypeOfValue(kRequiresKey, CFArrayGetTypeID());
75
76
return TRUE;
77
}
78
return FALSE;
79
}
80
81
/*
82
* remove item from waiting list
83
*/
84
void RemoveItemFromWaitingList(StartupContext aStartupContext, CFMutableDictionaryRef anItem)
85
{
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);
90
91
if (anIndex >= 0) {
92
CFArrayRemoveValueAtIndex(aStartupContext->aWaitingList, anIndex);
93
}
94
}
95
}
96
97
/*
98
* add item to failed list, create list if it doesn't exist
99
* return and fail quietly if it can't create list
100
*/
101
void AddItemToFailedList(StartupContext aStartupContext, CFMutableDictionaryRef anItem)
102
{
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);
107
}
108
if (aStartupContext->aFailedList) {
109
CFArrayAppendValue(aStartupContext->aFailedList, anItem);
110
}
111
}
112
}
113
114
/**
115
* startupItemListCopyMatches returns an array of items which contain the string aService in the key aKey
116
**/
117
static CFMutableArrayRef startupItemListCopyMatches(CFArrayRef anItemList, CFStringRef aKey, CFStringRef aService)
118
{
119
CFMutableArrayRef aResult = NULL;
120
121
if (anItemList && aKey && aService) {
122
CFIndex anItemCount = CFArrayGetCount(anItemList);
123
CFIndex anItemIndex = 0;
124
125
aResult = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
126
127
for (anItemIndex = 0; anItemIndex < anItemCount; ++anItemIndex) {
128
CFMutableDictionaryRef anItem = (CFMutableDictionaryRef) CFArrayGetValueAtIndex(anItemList, anItemIndex);
129
CFArrayRef aList = CFDictionaryGetValue(anItem, aKey);
130
131
if (aList) {
132
if (CFArrayContainsValue(aList, CFRangeMake(0, CFArrayGetCount(aList)), aService) &&
133
!CFArrayContainsValue(aResult, CFRangeMake(0, CFArrayGetCount(aResult)), anItem)) {
134
CFArrayAppendValue(aResult, anItem);
135
}
136
}
137
}
138
}
139
return aResult;
140
}
141
142
static void SpecialCasesStartupItemHandler(CFMutableDictionaryRef aConfig)
143
{
144
static const CFStringRef stubitems[] = {
145
CFSTR("Accounting"),
146
CFSTR("System Tuning"),
147
CFSTR("SecurityServer"),
148
CFSTR("Portmap"),
149
CFSTR("System Log"),
150
CFSTR("Resolver"),
151
CFSTR("LDAP"),
152
CFSTR("NetInfo"),
153
CFSTR("NetworkExtensions"),
154
CFSTR("DirectoryServices"),
155
CFSTR("Network Configuration"),
156
CFSTR("mDNSResponder"),
157
CFSTR("Cron"),
158
CFSTR("Core Graphics"),
159
CFSTR("Core Services"),
160
CFSTR("Network"),
161
CFSTR("TIM"),
162
CFSTR("Disks"),
163
CFSTR("NIS"),
164
NULL
165
};
166
CFMutableArrayRef aList, aNewList;
167
CFIndex i, aCount;
168
CFStringRef ci, type = kRequiresKey;
169
const CFStringRef *c;
170
171
again:
172
aList = (CFMutableArrayRef) CFDictionaryGetValue(aConfig, type);
173
if (aList) {
174
aCount = CFArrayGetCount(aList);
175
176
aNewList = CFArrayCreateMutable(kCFAllocatorDefault, aCount, &kCFTypeArrayCallBacks);
177
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++) {
182
if (CFEqual(*c, ci))
183
break;
184
}
185
if (*c == NULL) {
186
CFArrayAppendValue(aNewList, ci);
187
CF_syslog(LOG_DEBUG, CFSTR("%@: Keeping %@"), type, ci);
188
}
189
}
190
191
CFDictionaryReplaceValue(aConfig, type, aNewList);
192
CFRelease(aNewList);
193
}
194
if (type == kUsesKey)
195
return;
196
type = kUsesKey;
197
goto again;
198
}
199
200
CFIndex StartupItemListCountServices(CFArrayRef anItemList)
201
{
202
CFIndex aResult = 0;
203
204
if (anItemList) {
205
CFIndex anItemCount = CFArrayGetCount(anItemList);
206
CFIndex anItemIndex = 0;
207
208
for (anItemIndex = 0; anItemIndex < anItemCount; ++anItemIndex) {
209
CFDictionaryRef anItem = CFArrayGetValueAtIndex(anItemList, anItemIndex);
210
CFArrayRef aProvidesList = CFDictionaryGetValue(anItem, kProvidesKey);
211
212
if (aProvidesList)
213
aResult += CFArrayGetCount(aProvidesList);
214
}
215
}
216
return aResult;
217
}
218
219
bool StartupItemSecurityCheck(const char *aPath)
220
{
221
static struct timeval boot_time;
222
struct stat aStatBuf;
223
bool r = true;
224
225
if (boot_time.tv_sec == 0) {
226
int mib[] = { CTL_KERN, KERN_BOOTTIME };
227
size_t boot_time_sz = sizeof(boot_time);
228
int rv;
229
230
rv = sysctl(mib, sizeof(mib) / sizeof(mib[0]), &boot_time, &boot_time_sz, NULL, 0);
231
232
assert(rv != -1);
233
assert(boot_time_sz == sizeof(boot_time));
234
}
235
236
/* should use lstatx_np() on Tiger? */
237
if (lstat(aPath, &aStatBuf) == -1) {
238
if (errno != ENOENT)
239
syslog(LOG_ERR, "lstat(\"%s\"): %m", aPath);
240
return false;
241
}
242
/*
243
* We check the boot time because of 5409386.
244
* We ignore the boot time if PPID != 1 because of 5503536.
245
*/
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);
248
return false;
249
}
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);
252
r = false;
253
}
254
if (aStatBuf.st_mode & S_IWOTH) {
255
syslog(LOG_WARNING, "\"%s\" failed security check: world writable", aPath);
256
r = false;
257
}
258
if (aStatBuf.st_mode & S_IWGRP) {
259
syslog(LOG_WARNING, "\"%s\" failed security check: group writable", aPath);
260
r = false;
261
}
262
if (aStatBuf.st_uid != 0) {
263
syslog(LOG_WARNING, "\"%s\" failed security check: not owned by UID 0", aPath);
264
r = false;
265
}
266
if (aStatBuf.st_gid != 0) {
267
syslog(LOG_WARNING, "\"%s\" failed security check: not owned by GID 0", aPath);
268
r = false;
269
}
270
if (r == false) {
271
mkdir(kFixerDir, ACCESSPERMS);
272
close(open(kFixerPath, O_RDWR|O_CREAT|O_NOCTTY, DEFFILEMODE));
273
}
274
return r;
275
}
276
277
CFMutableArrayRef StartupItemListCreateWithMask(NSSearchPathDomainMask aMask)
278
{
279
CFMutableArrayRef anItemList = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
280
281
char aPath[PATH_MAX];
282
CFIndex aDomainIndex = 0;
283
284
NSSearchPathEnumerationState aState = NSStartSearchPathEnumeration(NSLibraryDirectory, aMask);
285
286
while ((aState = NSGetNextSearchPathEnumeration(aState, aPath))) {
287
DIR *aDirectory;
288
289
strlcat(aPath, kStartupItemsPath, sizeof(aPath));
290
++aDomainIndex;
291
292
/* 5485016
293
*
294
* Just in case...
295
*/
296
mkdir(aPath, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH);
297
298
if (!StartupItemSecurityCheck(aPath))
299
continue;
300
301
if ((aDirectory = opendir(aPath))) {
302
struct dirent *aBundle;
303
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];
311
312
if (aBundleName[0] == '.')
313
continue;
314
315
syslog(LOG_DEBUG, "Found item: %s", aBundleName);
316
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);
321
322
if (lstat(aDisabledFile, &aStatBuf) == 0) {
323
syslog(LOG_NOTICE, "Skipping disabled StartupItem: %s", aBundlePath);
324
continue;
325
}
326
if (!StartupItemSecurityCheck(aBundlePath))
327
continue;
328
if (!StartupItemSecurityCheck(aBundleExecutablePath))
329
continue;
330
if (!StartupItemSecurityCheck(aConfigFile))
331
continue;
332
333
/* Stow away the plist data for each bundle */
334
{
335
int aConfigFileDescriptor;
336
337
if ((aConfigFileDescriptor = open(aConfigFile, O_RDONLY|O_NOCTTY, (mode_t) 0)) != -1) {
338
struct stat aConfigFileStatBuffer;
339
340
if (stat(aConfigFile, &aConfigFileStatBuffer) != -1) {
341
off_t aConfigFileContentsSize = aConfigFileStatBuffer.st_size;
342
char *aConfigFileContentsBuffer;
343
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;
350
351
aConfigData =
352
CFDataCreateWithBytesNoCopy(NULL,
353
(const UInt8 *)aConfigFileContentsBuffer,
354
aConfigFileContentsSize,
355
kCFAllocatorNull);
356
357
if (aConfigData) {
358
aConfig = (CFMutableDictionaryRef)
359
CFPropertyListCreateFromXMLData(NULL, aConfigData,
360
kCFPropertyListMutableContainers,
361
NULL);
362
}
363
if (StartupItemValidate(aConfig)) {
364
CFStringRef aBundlePathString =
365
CFStringCreateWithCString(NULL, aBundlePath,
366
kCFStringEncodingUTF8);
367
368
CFNumberRef aDomainNumber =
369
CFNumberCreate(NULL, kCFNumberCFIndexType,
370
&aDomainIndex);
371
372
CFDictionarySetValue(aConfig, kBundlePathKey,
373
aBundlePathString);
374
CFDictionarySetValue(aConfig, kDomainKey, aDomainNumber);
375
CFRelease(aDomainNumber);
376
SpecialCasesStartupItemHandler(aConfig);
377
CFArrayAppendValue(anItemList, aConfig);
378
379
CFRelease(aBundlePathString);
380
} else {
381
syslog(LOG_ERR, "Malformatted parameters file: %s",
382
aConfigFile);
383
}
384
385
if (aConfig)
386
CFRelease(aConfig);
387
if (aConfigData)
388
CFRelease(aConfigData);
389
390
if (munmap(aConfigFileContentsBuffer, aConfigFileContentsSize) ==
391
-1) {
392
syslog(LOG_WARNING,
393
"Unable to unmap parameters file %s for item %s: %m",
394
aConfigFile, aBundleName);
395
}
396
} else {
397
syslog(LOG_ERR,
398
"Unable to map parameters file %s for item %s: %m",
399
aConfigFile, aBundleName);
400
}
401
} else {
402
syslog(LOG_ERR, "Unable to stat parameters file %s for item %s: %m",
403
aConfigFile, aBundleName);
404
}
405
406
if (close(aConfigFileDescriptor) == -1) {
407
syslog(LOG_ERR, "Unable to close parameters file %s for item %s: %m",
408
aConfigFile, aBundleName);
409
}
410
} else {
411
syslog(LOG_ERR, "Unable to open parameters file %s for item %s: %m", aConfigFile,
412
aBundleName);
413
}
414
}
415
}
416
if (closedir(aDirectory) == -1) {
417
syslog(LOG_WARNING, "Unable to directory bundle %s: %m", aPath);
418
}
419
} else {
420
if (errno != ENOENT) {
421
syslog(LOG_WARNING, "Open on directory %s failed: %m", aPath);
422
return (NULL);
423
}
424
}
425
}
426
427
return anItemList;
428
}
429
430
CFMutableDictionaryRef StartupItemListGetProvider(CFArrayRef anItemList, CFStringRef aService)
431
{
432
CFMutableDictionaryRef aResult = NULL;
433
CFMutableArrayRef aList = startupItemListCopyMatches(anItemList, kProvidesKey, aService);
434
435
if (aList && CFArrayGetCount(aList) > 0)
436
aResult = (CFMutableDictionaryRef) CFArrayGetValueAtIndex(aList, 0);
437
438
if (aList) CFRelease(aList);
439
440
return aResult;
441
}
442
443
CFArrayRef StartupItemListCreateFromRunning(CFArrayRef anItemList)
444
{
445
CFMutableArrayRef aResult = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
446
if (aResult) {
447
CFIndex anIndex, aCount = CFArrayGetCount(anItemList);
448
for (anIndex = 0; anIndex < aCount; ++anIndex) {
449
CFDictionaryRef anItem = CFArrayGetValueAtIndex(anItemList, anIndex);
450
if (anItem) {
451
CFNumberRef aPID = CFDictionaryGetValue(anItem, kPIDKey);
452
if (aPID)
453
CFArrayAppendValue(aResult, anItem);
454
}
455
}
456
}
457
return aResult;
458
}
459
460
/*
461
* Append items in anItemList to aDependents which depend on
462
* aParentItem.
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.
467
*/
468
static void appendDependents(CFMutableArrayRef aDependents, CFArrayRef anItemList, CFDictionaryRef aParentItem, Action anAction)
469
{
470
CFStringRef anInnerKey, anOuterKey;
471
CFArrayRef anOuterList;
472
473
/* Append the parent item to the list (avoiding duplicates) */
474
if (!CFArrayContainsValue(aDependents, CFRangeMake(0, CFArrayGetCount(aDependents)), aParentItem))
475
CFArrayAppendValue(aDependents, aParentItem);
476
477
/**
478
* Recursively append any children of the parent item for kStartAction and kStopAction.
479
* Do nothing for other actions.
480
**/
481
switch (anAction) {
482
case kActionStart:
483
anInnerKey = kProvidesKey;
484
anOuterKey = kRequiresKey;
485
break;
486
case kActionStop:
487
anInnerKey = kRequiresKey;
488
anOuterKey = kProvidesKey;
489
break;
490
default:
491
return;
492
}
493
494
anOuterList = CFDictionaryGetValue(aParentItem, anOuterKey);
495
496
if (anOuterList) {
497
CFIndex anOuterCount = CFArrayGetCount(anOuterList);
498
CFIndex anOuterIndex;
499
500
for (anOuterIndex = 0; anOuterIndex < anOuterCount; anOuterIndex++) {
501
CFStringRef anOuterElement = CFArrayGetValueAtIndex(anOuterList, anOuterIndex);
502
CFIndex anItemCount = CFArrayGetCount(anItemList);
503
CFIndex anItemIndex;
504
505
for (anItemIndex = 0; anItemIndex < anItemCount; anItemIndex++) {
506
CFDictionaryRef anItem = CFArrayGetValueAtIndex(anItemList, anItemIndex);
507
CFArrayRef anInnerList = CFDictionaryGetValue(anItem, anInnerKey);
508
509
if (anInnerList &&
510
CFArrayContainsValue(anInnerList, CFRangeMake(0, CFArrayGetCount(anInnerList)),
511
anOuterElement)
512
&& !CFArrayContainsValue(aDependents, CFRangeMake(0, CFArrayGetCount(aDependents)), anItem))
513
appendDependents(aDependents, anItemList, anItem, anAction);
514
}
515
}
516
}
517
}
518
519
CFMutableArrayRef StartupItemListCreateDependentsList(CFMutableArrayRef anItemList, CFStringRef aService, Action anAction)
520
{
521
CFMutableArrayRef aDependents = NULL;
522
CFMutableDictionaryRef anItem = NULL;
523
524
if (anItemList && aService)
525
anItem = StartupItemListGetProvider(anItemList, aService);
526
527
if (anItem) {
528
switch (anAction) {
529
case kActionRestart:
530
case kActionStart:
531
case kActionStop:
532
aDependents = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
533
534
if (!aDependents) {
535
CF_syslog(LOG_EMERG, CFSTR("Failed to allocate dependancy list for item %@"), anItem);
536
return NULL;
537
}
538
appendDependents(aDependents, anItemList, anItem, anAction);
539
break;
540
541
default:
542
break;
543
}
544
}
545
return aDependents;
546
}
547
548
/**
549
* countUnmetRequirements counts the number of items in anItemList
550
* which are pending in aStatusDict.
551
**/
552
static int countUnmetRequirements(CFDictionaryRef aStatusDict, CFArrayRef anItemList)
553
{
554
int aCount = 0;
555
CFIndex anItemCount = CFArrayGetCount(anItemList);
556
CFIndex anItemIndex;
557
558
for (anItemIndex = 0; anItemIndex < anItemCount; anItemIndex++) {
559
CFStringRef anItem = CFArrayGetValueAtIndex(anItemList, anItemIndex);
560
CFStringRef aStatus = CFDictionaryGetValue(aStatusDict, anItem);
561
562
if (!aStatus || !CFEqual(aStatus, kRunSuccess)) {
563
CF_syslog(LOG_DEBUG, CFSTR("\tFailed requirement/uses: %@"), anItem);
564
aCount++;
565
}
566
}
567
568
return aCount;
569
}
570
571
/**
572
* countDependantsPresent counts the number of items in aWaitingList
573
* which depend on items in anItemList.
574
**/
575
static int countDependantsPresent(CFArrayRef aWaitingList, CFArrayRef anItemList, CFStringRef aKey)
576
{
577
int aCount = 0;
578
CFIndex anItemCount = CFArrayGetCount(anItemList);
579
CFIndex anItemIndex;
580
581
for (anItemIndex = 0; anItemIndex < anItemCount; anItemIndex++) {
582
CFStringRef anItem = CFArrayGetValueAtIndex(anItemList, anItemIndex);
583
CFArrayRef aMatchesList = startupItemListCopyMatches(aWaitingList, aKey, anItem);
584
585
if (aMatchesList) {
586
aCount = aCount + CFArrayGetCount(aMatchesList);
587
CFRelease(aMatchesList);
588
}
589
}
590
591
return aCount;
592
}
593
594
/**
595
* pendingAntecedents returns TRUE if any antecedents of this item
596
* are currently running, have not yet run, or none exist.
597
**/
598
static Boolean
599
pendingAntecedents(CFArrayRef aWaitingList, CFDictionaryRef aStatusDict, CFArrayRef anAntecedentList, Action anAction)
600
{
601
int aPendingFlag = FALSE;
602
603
CFIndex anAntecedentCount = CFArrayGetCount(anAntecedentList);
604
CFIndex anAntecedentIndex;
605
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);
610
611
if (aMatchesList) {
612
CFIndex aMatchesListCount = CFArrayGetCount(aMatchesList);
613
CFIndex aMatchesListIndex;
614
615
for (aMatchesListIndex = 0; aMatchesListIndex < aMatchesListCount; ++aMatchesListIndex) {
616
CFDictionaryRef anItem = CFArrayGetValueAtIndex(aMatchesList, aMatchesListIndex);
617
618
if (!anItem ||
619
!CFDictionaryGetValue(anItem, kPIDKey) || !CFDictionaryGetValue(aStatusDict, anAntecedent)) {
620
aPendingFlag = TRUE;
621
break;
622
}
623
}
624
625
CFRelease(aMatchesList);
626
627
if (aPendingFlag)
628
break;
629
}
630
}
631
return (aPendingFlag);
632
}
633
634
/**
635
* checkForDuplicates returns TRUE if an item provides the same service as a
636
* pending item, or an item that already succeeded.
637
**/
638
static Boolean checkForDuplicates(CFArrayRef aWaitingList, CFDictionaryRef aStatusDict, CFDictionaryRef anItem)
639
{
640
int aDuplicateFlag = FALSE;
641
642
CFArrayRef aProvidesList = CFDictionaryGetValue(anItem, kProvidesKey);
643
CFIndex aProvidesCount = aProvidesList ? CFArrayGetCount(aProvidesList) : 0;
644
CFIndex aProvidesIndex;
645
646
for (aProvidesIndex = 0; aProvidesIndex < aProvidesCount; ++aProvidesIndex) {
647
CFStringRef aProvides = CFArrayGetValueAtIndex(aProvidesList, aProvidesIndex);
648
649
/* If the service succeeded, return true. */
650
CFStringRef aStatus = CFDictionaryGetValue(aStatusDict, aProvides);
651
if (aStatus && CFEqual(aStatus, kRunSuccess)) {
652
aDuplicateFlag = TRUE;
653
break;
654
}
655
/*
656
* Otherwise test if any item is currently running which
657
* might provide that service.
658
*/
659
else {
660
CFArrayRef aMatchesList = startupItemListCopyMatches(aWaitingList, kProvidesKey, aProvides);
661
if (aMatchesList) {
662
CFIndex aMatchesListCount = CFArrayGetCount(aMatchesList);
663
CFIndex aMatchesListIndex;
664
665
for (aMatchesListIndex = 0; aMatchesListIndex < aMatchesListCount; ++aMatchesListIndex) {
666
CFDictionaryRef anDupItem = CFArrayGetValueAtIndex(aMatchesList, aMatchesListIndex);
667
if (anDupItem && CFDictionaryGetValue(anDupItem, kPIDKey)) {
668
/*
669
* Item is running, avoid
670
* race condition.
671
*/
672
aDuplicateFlag = TRUE;
673
break;
674
} else {
675
CFNumberRef anItemDomain = CFDictionaryGetValue(anItem, kDomainKey);
676
CFNumberRef anotherItemDomain = CFDictionaryGetValue(anDupItem, kDomainKey);
677
/*
678
* If anItem was found later
679
* than aDupItem, stall
680
* anItem until aDupItem
681
* runs.
682
*/
683
if (anItemDomain &&
684
anotherItemDomain &&
685
CFNumberCompare(anItemDomain, anotherItemDomain,
686
NULL) == kCFCompareGreaterThan) {
687
/*
688
* Item not running,
689
* but takes
690
* precedence.
691
*/
692
aDuplicateFlag = TRUE;
693
break;
694
}
695
}
696
}
697
698
CFRelease(aMatchesList);
699
if (aDuplicateFlag)
700
break;
701
}
702
}
703
}
704
return (aDuplicateFlag);
705
}
706
707
CFMutableDictionaryRef StartupItemListGetNext(CFArrayRef aWaitingList, CFDictionaryRef aStatusDict, Action anAction)
708
{
709
CFMutableDictionaryRef aNextItem = NULL;
710
CFIndex aWaitingCount = CFArrayGetCount(aWaitingList);
711
int aMinFailedAntecedents = INT_MAX;
712
CFIndex aWaitingIndex;
713
714
switch (anAction) {
715
case kActionStart:
716
break;
717
case kActionStop:
718
break;
719
case kActionRestart:
720
break;
721
default:
722
return NULL;
723
}
724
725
if (!aWaitingList || !aStatusDict || aWaitingCount <= 0)
726
return NULL;
727
728
/**
729
* Iterate through the items in aWaitingList and look for an optimally ready item.
730
**/
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
735
* depenancies */
736
Boolean aBestPick = FALSE; /* Is this the best pick
737
* so far? */
738
739
/* Filter out running items. */
740
if (CFDictionaryGetValue(anItem, kPIDKey))
741
continue;
742
743
/*
744
* Filter out dupilicate services; if someone has
745
* provided what we provide, we don't run.
746
*/
747
if (checkForDuplicates(aWaitingList, aStatusDict, anItem)) {
748
CF_syslog(LOG_DEBUG, CFSTR("Skipping %@ because of duplicate service."),
749
CFDictionaryGetValue(anItem, kDescriptionKey));
750
continue;
751
}
752
/*
753
* Dependencies don't matter when restarting an item;
754
* stop here.
755
*/
756
if (anAction == kActionRestart) {
757
aNextItem = anItem;
758
break;
759
}
760
anAntecedentList = CFDictionaryGetValue(anItem, ((anAction == kActionStart) ? kRequiresKey : kProvidesKey));
761
762
CF_syslog(LOG_DEBUG, CFSTR("Checking %@"), CFDictionaryGetValue(anItem, kDescriptionKey));
763
764
if (anAntecedentList)
765
CF_syslog(LOG_DEBUG, CFSTR("Antecedents: %@"), anAntecedentList);
766
else
767
syslog(LOG_DEBUG, "No antecedents");
768
769
/**
770
* Filter out the items which have unsatisfied antecedents.
771
**/
772
if (anAntecedentList &&
773
((anAction == kActionStart) ?
774
countUnmetRequirements(aStatusDict, anAntecedentList) :
775
countDependantsPresent(aWaitingList, anAntecedentList, kRequiresKey)))
776
continue;
777
778
/**
779
* anItem has all hard dependancies met; check for soft dependancies.
780
* We'll favor the item with the fewest unmet soft dependancies here.
781
**/
782
anAntecedentList = CFDictionaryGetValue(anItem, ((anAction == kActionStart) ? kUsesKey : kProvidesKey));
783
784
if (anAntecedentList)
785
CF_syslog(LOG_DEBUG, CFSTR("Soft dependancies: %@"), anAntecedentList);
786
else
787
syslog(LOG_DEBUG, "No soft dependancies");
788
789
if (anAntecedentList) {
790
aFailedAntecedentsCount =
791
((anAction == kActionStart) ?
792
countUnmetRequirements(aStatusDict, anAntecedentList) :
793
countDependantsPresent(aWaitingList, anAntecedentList, kUsesKey));
794
} else {
795
if (aMinFailedAntecedents > 0)
796
aBestPick = TRUE;
797
}
798
799
/*
800
* anItem has unmet dependencies that will
801
* likely be met in the future, so delay it
802
*/
803
if (aFailedAntecedentsCount > 0 && pendingAntecedents(aWaitingList, aStatusDict, anAntecedentList, anAction)) {
804
continue;
805
}
806
if (aFailedAntecedentsCount > 0)
807
syslog(LOG_DEBUG, "Total: %d", aFailedAntecedentsCount);
808
809
if (aFailedAntecedentsCount > aMinFailedAntecedents)
810
continue; /* Another item already won out */
811
812
if (aFailedAntecedentsCount < aMinFailedAntecedents)
813
aBestPick = TRUE;
814
815
if (!aBestPick)
816
continue;
817
818
/*
819
* anItem has less unmet
820
* dependancies than any
821
* other item so far, so it
822
* wins.
823
*/
824
syslog(LOG_DEBUG, "Best pick so far, based on failed dependancies (%d->%d)",
825
aMinFailedAntecedents, aFailedAntecedentsCount);
826
827
/*
828
* We have a winner! Update success
829
* parameters to match anItem.
830
*/
831
aMinFailedAntecedents = aFailedAntecedentsCount;
832
aNextItem = anItem;
833
834
} /* End of waiting list loop. */
835
836
return aNextItem;
837
}
838
839
CFStringRef StartupItemCreateDescription(CFMutableDictionaryRef anItem)
840
{
841
CFStringRef aString = NULL;
842
843
if (anItem)
844
aString = CFDictionaryGetValue(anItem, kDescriptionKey);
845
if (aString)
846
CFRetain(aString);
847
return aString;
848
}
849
850
pid_t StartupItemGetPID(CFDictionaryRef anItem)
851
{
852
CFIndex anItemPID = 0;
853
CFNumberRef aPIDNumber = anItem ? CFDictionaryGetValue(anItem, kPIDKey) : NULL;
854
if (aPIDNumber && CFNumberGetValue(aPIDNumber, kCFNumberCFIndexType, &anItemPID))
855
return (pid_t) anItemPID;
856
else
857
return 0;
858
}
859
860
CFMutableDictionaryRef StartupItemWithPID(CFArrayRef anItemList, pid_t aPID)
861
{
862
CFIndex anItemCount = CFArrayGetCount(anItemList);
863
CFIndex anItemIndex;
864
865
for (anItemIndex = 0; anItemIndex < anItemCount; anItemIndex++) {
866
CFMutableDictionaryRef anItem = (CFMutableDictionaryRef) CFArrayGetValueAtIndex(anItemList, anItemIndex);
867
CFNumberRef aPIDNumber = CFDictionaryGetValue(anItem, kPIDKey);
868
CFIndex anItemPID;
869
870
if (aPIDNumber) {
871
CFNumberGetValue(aPIDNumber, kCFNumberCFIndexType, &anItemPID);
872
873
if ((pid_t) anItemPID == aPID)
874
return anItem;
875
}
876
}
877
878
return NULL;
879
}
880
881
int StartupItemRun(CFMutableDictionaryRef aStatusDict, CFMutableDictionaryRef anItem, Action anAction)
882
{
883
int anError = -1;
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 */
892
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 */
903
NULL
904
};
905
const CFStringRef *c;
906
907
if (aProvidesList && anAction == kActionStop) {
908
CFIndex aProvidesCount = CFArrayGetCount(aProvidesList);
909
for (c = stubitems; *c; c++) {
910
if (CFArrayContainsValue(aProvidesList, CFRangeMake(0, aProvidesCount), *c)) {
911
CFIndex aPID = -1;
912
CFNumberRef aProcessNumber = CFNumberCreate(NULL, kCFNumberCFIndexType, &aPID);
913
914
CFDictionarySetValue(anItem, kPIDKey, aProcessNumber);
915
CFRelease(aProcessNumber);
916
917
StartupItemExit(aStatusDict, anItem, TRUE);
918
return -1;
919
}
920
}
921
}
922
923
if (anAction == kActionNone) {
924
StartupItemExit(aStatusDict, anItem, TRUE);
925
anError = 0;
926
} else {
927
CFStringRef aBundlePathString = CFDictionaryGetValue(anItem, kBundlePathKey);
928
char aBundlePath[PATH_MAX];
929
char anExecutable[PATH_MAX];
930
char *tmp;
931
932
if (!CFStringGetCString(aBundlePathString, aBundlePath, sizeof(aBundlePath), kCFStringEncodingUTF8)) {
933
CF_syslog(LOG_EMERG, CFSTR("Internal error while running item %@"), aBundlePathString);
934
return (anError);
935
}
936
/* Compute path to excecutable */
937
tmp = rindex(aBundlePath, '/');
938
snprintf(anExecutable, sizeof(anExecutable), "%s%s", aBundlePath, tmp);
939
940
/**
941
* Run the bundle
942
**/
943
944
if (access(anExecutable, X_OK)) {
945
/*
946
* Add PID key so that this item is marked as having
947
* been run.
948
*/
949
CFIndex aPID = -1;
950
CFNumberRef aProcessNumber = CFNumberCreate(NULL, kCFNumberCFIndexType, &aPID);
951
952
CFDictionarySetValue(anItem, kPIDKey, aProcessNumber);
953
CFRelease(aProcessNumber);
954
955
CFDictionarySetValue(anItem, kErrorKey, kErrorPermissions);
956
StartupItemExit(aStatusDict, anItem, FALSE);
957
syslog(LOG_ERR, "No executable file %s", anExecutable);
958
} else {
959
pid_t aProccessID = fork();
960
961
switch (aProccessID) {
962
case -1: /* SystemStarter (fork failed) */
963
CFDictionarySetValue(anItem, kErrorKey, kErrorFork);
964
StartupItemExit(aStatusDict, anItem, FALSE);
965
966
CF_syslog(LOG_ERR, CFSTR("Failed to fork for item %@: %s"), aBundlePathString, strerror(errno));
967
968
break;
969
970
default: /* SystemStarter (fork succeeded) */
971
{
972
CFIndex aPID = (CFIndex) aProccessID;
973
CFNumberRef aProcessNumber = CFNumberCreate(NULL, kCFNumberCFIndexType, &aPID);
974
975
CFDictionarySetValue(anItem, kPIDKey, aProcessNumber);
976
CFRelease(aProcessNumber);
977
978
syslog(LOG_DEBUG, "Running command (%d): %s %s",
979
aProccessID, anExecutable, argumentForAction(anAction));
980
anError = 0;
981
}
982
break;
983
984
case 0: /* Child */
985
{
986
if (setsid() == -1)
987
syslog(LOG_WARNING, "Unable to create session for item %s: %m", anExecutable);
988
989
anError = execl(anExecutable, anExecutable, argumentForAction(anAction), NULL);
990
991
/* We shouldn't get here. */
992
993
syslog(LOG_ERR, "execl(\"%s\"): %m", anExecutable);
994
995
exit(anError);
996
}
997
}
998
}
999
}
1000
1001
return (anError);
1002
}
1003
1004
void
1005
StartupItemSetStatus(CFMutableDictionaryRef aStatusDict, CFMutableDictionaryRef anItem, CFStringRef aServiceName,
1006
Boolean aSuccess, Boolean aReplaceFlag)
1007
{
1008
void (*anAction) (CFMutableDictionaryRef, const void *, const void *) = aReplaceFlag ?
1009
CFDictionarySetValue : CFDictionaryAddValue;
1010
1011
if (aStatusDict && anItem) {
1012
CFArrayRef aProvidesList = CFDictionaryGetValue(anItem, kProvidesKey);
1013
if (aProvidesList) {
1014
CFIndex aProvidesCount = CFArrayGetCount(aProvidesList);
1015
CFIndex aProvidesIndex;
1016
1017
/*
1018
* If a service name was specified, and it is valid,
1019
* use only it.
1020
*/
1021
if (aServiceName && CFArrayContainsValue(aProvidesList, CFRangeMake(0, aProvidesCount), aServiceName)) {
1022
aProvidesList = CFArrayCreate(NULL, (const void **)&aServiceName, 1, &kCFTypeArrayCallBacks);
1023
aProvidesCount = 1;
1024
} else {
1025
CFRetain(aProvidesList);
1026
}
1027
1028
for (aProvidesIndex = 0; aProvidesIndex < aProvidesCount; aProvidesIndex++) {
1029
CFStringRef aService = CFArrayGetValueAtIndex(aProvidesList, aProvidesIndex);
1030
1031
if (aSuccess)
1032
anAction(aStatusDict, aService, kRunSuccess);
1033
else
1034
anAction(aStatusDict, aService, kRunFailure);
1035
}
1036
1037
CFRelease(aProvidesList);
1038
}
1039
}
1040
}
1041
1042
void StartupItemExit(CFMutableDictionaryRef aStatusDict, CFMutableDictionaryRef anItem, Boolean aSuccess)
1043
{
1044
StartupItemSetStatus(aStatusDict, anItem, NULL, aSuccess, FALSE);
1045
}
1046
1047